<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thorsten Hirsch</title>
    <description>The latest articles on Forem by Thorsten Hirsch (@thorstenhirsch).</description>
    <link>https://forem.com/thorstenhirsch</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2805%2F619741.jpeg</url>
      <title>Forem: Thorsten Hirsch</title>
      <link>https://forem.com/thorstenhirsch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thorstenhirsch"/>
    <language>en</language>
    <item>
      <title>Why Scale To Zero?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Tue, 22 Apr 2025 11:16:32 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/why-scale-to-zero-59ph</link>
      <guid>https://forem.com/thorstenhirsch/why-scale-to-zero-59ph</guid>
      <description>&lt;p&gt;What's the point of scaling to zero? Seriously, I don't understand why anybody would want to consider this as a goal in their architecture as it complicates things for nearly zero benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Savings Are Close To Zero
&lt;/h2&gt;

&lt;p&gt;Let's take a microservice application with a database, a message broker, several frontend and backend containers. Some parts scale up easily, others are more complicated, but the point is that you need at least 1 instance of each component for your whole application to work properly. We also address fault tolerance by only using managed services and a Kubernetes cluster with multiple nodes.&lt;/p&gt;

&lt;p&gt;The costs could look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$ 1,700 Kubernetes Cluster (3 nodes)&lt;/li&gt;
&lt;li&gt;$ 100 Message Broker (as Service)&lt;/li&gt;
&lt;li&gt;$ 150 Database (as Service)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sum: $ 1,950&lt;/p&gt;

&lt;p&gt;If this setup is enough for 10,000 users, we scale up for 1,000,000 users (Congratulations!) with this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$ 28,000 Kubernetes Cluster (50 nodes)&lt;/li&gt;
&lt;li&gt;$ 10,000 Message Broker (as Service)&lt;/li&gt;
&lt;li&gt;$ 15,000 Database (as Service)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sum: $ 53,000&lt;/p&gt;

&lt;p&gt;...and we can scale back down to $ 1,950 just like that. No architectural change is needed, just shutdown the additional instances we used for scaling up to 1,000,000 users and we save 96,3 % of our costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling to zero would only save an additional 3,7 %&lt;/strong&gt;, which is equivalent to going from 960,000 users to 1,000,000 users. Did we think about this step the same way as we think about scaling from one to zero? Probably not, because it's too small - we rather focused on scaling up from 800,000 to 1,000,000 users or something like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep The Architecture Simple
&lt;/h2&gt;

&lt;p&gt;Scaling up and down between the least number of instances and what's required for 1,000,000 users does not require any architectural change. Just keep the bare minimum of instances running and as soon as traffic goes up Kubernetes is able to (auto-) scale horizontally. But scaling to zero is a different kind of beast. It means you really shutdown components completely and need to wake them up somehow, e.g. by issuing an API call to Kubernetes to start the very 1st container of your frontend. This requires additional tooling or additional components. Think about the following insane approach of scaling to zero:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's a worker queue in your message broker with a task waiting to be processed, but since you scaled down your worker containers to zero you now need to implement some kind of monitor that checks all queues for any waiting messages, which is then able to scale up the corresponding container from zero to one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please don't even think about shutting down your message broker if your monitor script finds all queues empty. I already heard people suggest building another message layer for such events just to inform another additional, always-running component that it now has to start the container with the "real" message broker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Test Environments?
&lt;/h2&gt;

&lt;p&gt;You don't want a different architecture in your test environment than in your production environment! So, you don't want to have additional components just to be able to scale to zero. If really nobody is using your test environment for quite a while, just shutdown it down completely and start it up manually as soon as you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Infrequent Usage?
&lt;/h2&gt;

&lt;p&gt;Do you mean user-driven infrequent usage? I'd say your minimal setup is still the best way to go. If the number of users is so small that they don't cover your server costs you better think about a way to attract more users instead of implementing scale-to-zero.&lt;/p&gt;

&lt;p&gt;And what about cronjobs that only run once a month? If this is the only usage of your application you can call yourself lucky, because then you can exactly plan your usage. Just startup and shutdown your environment in the same cronjob. Notice that a planned shutdown is not what it means for an application to be able to (automatically!) scale-to-zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I Have A Clue?
&lt;/h2&gt;

&lt;p&gt;Not really, so I just googled "scale to zero" and found &lt;a href="https://www.reddit.com/r/kubernetes/comments/1de8qiz/scaling_to_zero/" rel="noopener noreferrer"&gt;this guy on Reddit&lt;/a&gt;, who explores options for scaling to zero in Kubernetes. The very first questions he asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What [additional] tools are required?&lt;/li&gt;
&lt;li&gt;What do they cost?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's exactly what I'm complaining about! No, it's worse: not only do these additional tools mess up your beautiful, simple architecture, they also eat up the tiny savings you could've achieved if they're not for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;p&gt;Please don't waste your time trying to make your application scalable to zero.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>kubernetes</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Who Writes Helm Charts?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Mon, 03 Jul 2023 18:03:03 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/who-writes-helm-charts-27lc</link>
      <guid>https://forem.com/thorstenhirsch/who-writes-helm-charts-27lc</guid>
      <description>&lt;p&gt;It seems like for Helm charts (and their values files) the whole IT department needs to provide input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are the dependencies of the application?&lt;/li&gt;
&lt;li&gt;What settings are available to customize the application?&lt;/li&gt;
&lt;li&gt;Server names, URL of DB, storage settings, maybe also message broker, and all other infrastructure/platform requirements&lt;/li&gt;
&lt;li&gt;Initial resource allocation, scaling settings, deployment strategy, HA settings (if on application level)&lt;/li&gt;
&lt;li&gt;Internal network settings like ports to be opened between your pods, service mesh settings &amp;amp; mTLS, certificates &amp;amp; their renewal&lt;/li&gt;
&lt;li&gt;Usernames and passwords in the different stages, path to secrets, additional certificates that need to be imported&lt;/li&gt;
&lt;li&gt;Backup settings, agents, storage path, interval settings&lt;/li&gt;
&lt;li&gt;Monitoring &amp;amp; logging agents, forwarding settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work at a small company you might only have 2 or 3 people filling all these roles. But imagine working at a big company, where you really have 10 different people. Is it even possible under these circumstances to write Helm charts (and their values files) for an application in a single sprint?&lt;/p&gt;

&lt;p&gt;So what's the solution? &lt;strong&gt;Who writes Helm charts (and their values files) at your workplace?&lt;/strong&gt; Is splitting responsibility between charts and values files a good start? Where do you draw the line between things that belong into Terraform scripts and things that go into Helm charts?&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Web5: A Better Web3 Approach?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sun, 12 Jun 2022 21:33:26 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/web5-a-better-web3-approach-29m3</link>
      <guid>https://forem.com/thorstenhirsch/web5-a-better-web3-approach-29m3</guid>
      <description>&lt;p&gt;tl;dr: On June 10 Jack Dorsey (ex Twitter CEO) announced Web5, which is basically Web3, but without the token scams.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blockchain Wars
&lt;/h2&gt;

&lt;p&gt;The crypto space is somehow divided in Bitcoin maximalists, who accept no other &lt;del&gt;god&lt;/del&gt; blockchain. Their position is that Bitcoin is the only valid use case for blockchains. It's also the only distributed and secure blockchain, as well as the only one that's not a Ponzi scheme. Unfortunately it is wasting enormous amounts of energy.&lt;/p&gt;

&lt;p&gt;On the other side we have Ethereum and all the more advanced blockchains, which are basically frameworks for building decentralised apps on top. The problem: many "apps" are just coins, whose developers might or might not be scammers, using the coins to fund their projects outside of the regulated investment channels. Most of these blockchains do not kill our planet, as they use other consensus mechanisms than Bitcoin. Well, Ethereum still does, but at least they admit it's a problem and try to migrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web5
&lt;/h2&gt;

&lt;p&gt;Jack Dorsey is a Bitcoin maxi, but he also likes the Web3 idea of decentralised apps with self-sovereign identity and user controlled data management for privacy. So he and his company TBD are now developing the building blocks for such applications in the Bitcoin space.&lt;/p&gt;

&lt;p&gt;The key difference to Web3 is: only identity is stored on-chain. Everything else is stored on user-run nodes. This means: no smart contracts, no "shitcoins" (I don't like this term), and no fraudulent tokenomics with air drops, yield farming, or staking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Blocks
&lt;/h2&gt;

&lt;p&gt;• &lt;strong&gt;Decentralised identifiers (DiDs)&lt;/strong&gt;. They are unique IDs for every user, like a public key (wallet address) in Web3. They are implemented as a Layer 2 protocol on Bitcoin (like Lightning) called ION.&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Verifiable credentials (VCs)&lt;/strong&gt; are additional information for each user. This might your home address, a digital signature, your age, or something else. AFAIK access can be implemented with ZK algorithms to protect privacy.&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Decentralised Web Nodes (DWNs)&lt;/strong&gt;. DWNs are decentralised computers. They store data and facilitate communication between DiDs. Analogous to smart contracts in Web3, but run locally (I guess this means on a single node) instead of "on the blockchain" (which means on every node).&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Decentralised Web Apps (DWAs)&lt;/strong&gt;. Users interact with Web5 using a DWA. This is like a web app with a key difference: users control their own data and there is no intermediary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Since applications cannot be implemented directly in Bitcoin (due to the lack of smart contracts) Web5 brings building blocks, such as an L2 protocol for identity and data management. This seems like a bad deal for developers, as everything gets more complex, but it might solve the problems and frauds we see in Web3 to some extent... so why not give it a try?&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Information
&lt;/h2&gt;

&lt;p&gt;You can read more about Web5 on TBD's project website: &lt;a href="https://developer.tbd.website/projects/web5/"&gt;https://developer.tbd.website/projects/web5/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They also have &lt;a href="https://developer.tbd.website/docs/Decentralized%20Web%20Platform%20-%20Public.pdf"&gt;slides (PDF)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;I have to thank &lt;a href="https://twitter.com/ntkris"&gt;@ntkris&lt;/a&gt;, because I copied a lot from his tweets for this article.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>bitcoin</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Rails 6 with Metaprogrammed Models</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sun, 06 Dec 2020 12:10:21 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/rails-6-with-metaprogrammed-models-5aik</link>
      <guid>https://forem.com/thorstenhirsch/rails-6-with-metaprogrammed-models-5aik</guid>
      <description>&lt;h1&gt;
  
  
  The Task
&lt;/h1&gt;

&lt;p&gt;In the last weeks I had to migrate several databases to a new one, which was mostly copying data, but also a bit of enriching data. It was just a little bit too much for plain SQL and since Rails 6 has multi database support I wanted to give it a try.&lt;/p&gt;

&lt;p&gt;Here's an example of the task:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvyutexsa4i7isctnoyh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvyutexsa4i7isctnoyh0.png" alt="Enrichiching the Person model with Country-of-Phone-Number attribute"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we already have both schemas, the one in the old database and the one in the new database. The table names are all the same in both databases, some models just have some additional fields in the new database like &lt;code&gt;persons.country_of_phone_number&lt;/code&gt;. We fill this new field during migration by fetching the country code from the phone number (e.g. "+49") and doing a lookup to which country this country code belongs (e.g. in a REST service or another table).&lt;/p&gt;
&lt;h1&gt;
  
  
  Not The Plan
&lt;/h1&gt;

&lt;p&gt;Since this migration is a one-time-procedure that has nothing to do with the evolution of the rails application &lt;strong&gt;we won't "pollute" the code of the rails application&lt;/strong&gt; with our migration task. Instead we will create a new rails app just for the migration.&lt;/p&gt;

&lt;p&gt;We will also &lt;strong&gt;not&lt;/strong&gt; copy/generate the model files from the existing rails application since we don't need any model functionality. We just want to copy data. But how does database access work without (ActiveRecord based) model files?&lt;/p&gt;
&lt;h1&gt;
  
  
  The Plan
&lt;/h1&gt;

&lt;p&gt;It's possible to generate model files on-the-fly just as we need them. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_class_if_not_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespaced_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namespaced_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_constantize&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
    &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namespaced_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/::/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namespaced_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/::/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="sx"&gt;%{
module &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;
  class &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt; &amp;lt; BaseRecord
  end
end
}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The indentation looks a bit ugly, but I like it better than using lots of "\n" in a single line string.&lt;/p&gt;

&lt;p&gt;So let's look a the code. The most important thing to notice is that we generate a class that inherits from BaseRecord &lt;strong&gt;in a module&lt;/strong&gt;. And that's the key to access different databases. So we need to make sure, that we have one module for the old database and another one for the new database.&lt;/p&gt;

&lt;h1&gt;
  
  
  Database Configuration
&lt;/h1&gt;

&lt;p&gt;Rails 6 has introduced one more level in &lt;code&gt;database.yml&lt;/code&gt; in order to define multiple databases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;new_db&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;newuser'&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;newpassword'&lt;/span&gt;
  &lt;span class="na"&gt;old_db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;old_db&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;olduser'&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oldpassword'&lt;/span&gt;
  &lt;span class="na"&gt;another_old_db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;another_old_db&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;olduser'&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oldpassword'&lt;/span&gt;
  &lt;span class="s"&gt;end&lt;/span&gt;
&lt;span class="s"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I recommend to use the primary database for the (single) new database, thus all old databases have specific names.&lt;/p&gt;

&lt;h1&gt;
  
  
  BaseRecords
&lt;/h1&gt;

&lt;p&gt;Now in order to tell ActiveRecord which database to use we have to implement the BaseRecord for each database. Since the new database uses the &lt;strong&gt;primary&lt;/strong&gt; db configuration we don't need to specify the database in &lt;code&gt;app/models/new_db/base_record.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;NewDb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But all old databases must be linked to their &lt;code&gt;database.yml&lt;/code&gt; configuration, so let's take a look at &lt;code&gt;app/models/old_db/base_record.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;OldDb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
    &lt;span class="n"&gt;connects_to&lt;/span&gt; &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: :old_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;reading: :old_db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for the setup.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Migration
&lt;/h1&gt;

&lt;p&gt;Now the migration is really easy. We can write a rake task for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'migrate all tables'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:migrate&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:environment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;%i( :persons :foos :bars )&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;migrate_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;migrate_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^:/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;source_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'OldDb::'&lt;/span&gt;
    &lt;span class="n"&gt;source_string&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&lt;/span&gt;
    &lt;span class="n"&gt;generate_class_if_not_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_constantize&lt;/span&gt;

    &lt;span class="n"&gt;target_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'NewDb::'&lt;/span&gt;
    &lt;span class="n"&gt;target_string&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&lt;/span&gt;
    &lt;span class="n"&gt;generate_class_if_not_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_constantize&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="c1"&gt;# first try to call a specific method for data enrichment&lt;/span&gt;
      &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"table_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_e&lt;/span&gt;
      &lt;span class="c1"&gt;# no specific method found means: no enrichment, just copy the data&lt;/span&gt;
      &lt;span class="n"&gt;normal_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have two cases here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tables that are 100% the same in old db and new db use &lt;code&gt;normal_table&lt;/code&gt; to just copy the data&lt;/li&gt;
&lt;li&gt;tables that need enrichment use a specific method, e.g. &lt;code&gt;table_persons&lt;/code&gt; for adding the Country-of-Phone-Number property; we need to implement a specific method for each table that needs enrichment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are both methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normal_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"migrating &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;except&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records migrated."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;table_persons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"migrating &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;except&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;country_of_phone_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some_magic_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records migrated."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. We can copy hundreds of tables from old_db to new_db with just two files under &lt;code&gt;app/models&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Discussion: Metaprogramming
&lt;/h1&gt;

&lt;p&gt;We always should ask ourselves: would another developer understand my codebase? There are several aspects we need to consider, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How big is my codebase?&lt;/li&gt;
&lt;li&gt;How readable is my code?&lt;/li&gt;
&lt;li&gt;How complex is my code?&lt;/li&gt;
&lt;li&gt;Do I make use of advanced techniques?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the plus side our migration project is a very minimalistic rails project with no legacy code and the least amount of self written code I can think of. But we have used advanced techniques like &lt;code&gt;send(:method, :param1, :param2)&lt;/code&gt; in order to call methods by names we determine at runtime (dynamically). This might be called "metaprogramming". There's even a book for that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwl82z0m3lqtkzig3u2n9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwl82z0m3lqtkzig3u2n9.jpg" alt="book cover of "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/" rel="noopener noreferrer"&gt;You can buy it here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the claims in this book is that metaprogramming in ruby is just programming, it's nothing special, because you don't need to access anything "forbidden". Other languages might have things like &lt;code&gt;__method_name__&lt;/code&gt; to access metaprogramming, which already tells you by the name "Caution! My double underscore prefix is reserved for language constructs. Here be dragons!"&lt;/p&gt;

&lt;p&gt;Ruby is different. Take a look at the object model, for example, it's a real beauty - &lt;a href="https://www.toptal.com/ruby/ruby-metaprogramming-cooler-than-it-sounds" rel="noopener noreferrer"&gt;go check it out!&lt;/a&gt; There's no need for ruby to draw a line between programming and metaprogramming, because ruby's advanced concepts still rely on simple, yet powerful foundations, that are easy to grasp.&lt;/p&gt;

&lt;p&gt;So I think my codebase is easy to understand mainly thanks to its brevity. You only have to know one or two advanced concepts. And IMHO that's much better than writing a lot of code explicitly, because the more code I write, the more bugs I write, and the more code another developer has to read.&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

&lt;p&gt;(&lt;a href="http://jasonheeley.deviantart.com/art/Final-Train-91220373" rel="noopener noreferrer"&gt;The cover image is "Final Train" by Jason Heeley.&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
      <category>webdev</category>
      <category>rails</category>
    </item>
    <item>
      <title>Looking for Angular Architecture Advise</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sat, 07 Nov 2020 11:13:44 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/looking-for-angular-architecture-advise-d7</link>
      <guid>https://forem.com/thorstenhirsch/looking-for-angular-architecture-advise-d7</guid>
      <description>&lt;p&gt;After building one or two Angular applications it's time to ask if I'm doing things right. There might be a lot of beginner tutorials for Angular out there, but it's hard to find &lt;strong&gt;architectural best practices&lt;/strong&gt;. My most urgent question is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How long to keep using data as an Observable? When can/should I "resolve" it to its actual data e.g. with the async pipe?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer might be "as long as possible", so I should always try to use an observable and only "resolve" it in the the html template where the data is finally being shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmyonbhmt2yx36w6nt3va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmyonbhmt2yx36w6nt3va.png" alt="resolve observables in the child components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I guess it's easier to use an observable as short as possible by resolving it in the parent component and providing the data as input for the child components like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fel0y21c0fbsp9epprqgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fel0y21c0fbsp9epprqgv.png" alt="resolve observables in the parent component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I really don't know the answer. And here are some more questions I keep asking myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since I only want to fetch data from my http server once, but use it several times - where do I use ReplaySubjects? In the Services or in the Components?&lt;/li&gt;
&lt;li&gt;Should I instantiate Observables/Subjects when declaring their variables? Or should I instantiate in the constructor?&lt;/li&gt;
&lt;li&gt;When it comes to subscribe/unsubscribe I should probably use the &lt;code&gt;ngOnInit()&lt;/code&gt; and &lt;code&gt;ngOnDestroy()&lt;/code&gt; hooks, which are available in Components only... so I guess I should never subscribe/unsubscribe in my services, right?&lt;/li&gt;
&lt;li&gt;Is it a bad pattern to have an Observable &lt;code&gt;foo$&lt;/code&gt; together with its resolved data &lt;code&gt;foo&lt;/code&gt; in the same class?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please share your recommendations!&lt;/p&gt;

&lt;p&gt;Attributions for the cover image go to &lt;a href="https://www.freepik.com/photos/background" rel="noopener noreferrer"&gt;jannoon028 from www.freepik.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>help</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Just JS (by Dan Abramov)</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sat, 08 Aug 2020 20:07:45 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/just-js-by-dan-abramov-1l76</link>
      <guid>https://forem.com/thorstenhirsch/just-js-by-dan-abramov-1l76</guid>
      <description>&lt;p&gt;This is a recommendation of a JS tutorial for beginners by Dan Abramov. It's called &lt;strong&gt;Just JS&lt;/strong&gt; and I liked it very much, especially for the visualisations by Maggie Appleton. The cover image of this article is one of hers.&lt;/p&gt;

&lt;p&gt;Unfortunately the tutorial is only available by mail and it's very shot, 9 courses only. But I can assure you: it's worth it. The mental model for how the inners of JS work are pure gold. &lt;a href="https://justjavascript.com"&gt;You can subscribe here on Dan's website for Just JS.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;P.S.: Dan Abramov is working on React and the (co-) creator of Redux and create-react-app.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Q: What Technologies Should I Use for Migrating a Rails Monolith to Microservices in 2020?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Mon, 23 Dec 2019 17:26:55 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/q-what-technologies-should-i-use-for-migrating-a-rails-monolith-to-microservices-in-2020-1j77</link>
      <guid>https://forem.com/thorstenhirsch/q-what-technologies-should-i-use-for-migrating-a-rails-monolith-to-microservices-in-2020-1j77</guid>
      <description>&lt;p&gt;So I have this 6 years old rails application monolith that contains all these things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user interface&lt;/li&gt;
&lt;li&gt;database&lt;/li&gt;
&lt;li&gt;additional tables for statistics (snowflake schema)&lt;/li&gt;
&lt;li&gt;API for android app&lt;/li&gt;
&lt;li&gt;workflow engine&lt;/li&gt;
&lt;li&gt;batch workers (sidekiq)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently I'm running 4 instances of this application, each is configured especially for the customer who's using the instance. Yes, that makes 4 customers, each has ~300 users, so we're talking about 1200 users. A virtual server with 2 cores and 8 GB RAM handles it easily. The web server is nginx and the ruby application server is passenger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In 2020 a new UI (SPA) will be written&lt;/strong&gt;, so I can get rid of the UI in rails. However I will have to provide an API for the SPA and it will be totally different than the API for the android app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a great opportunity to split the monolith into microservices!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I might split the microservices like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API for new UI&lt;/li&gt;
&lt;li&gt;API for android app&lt;/li&gt;
&lt;li&gt;batch workers&lt;/li&gt;
&lt;li&gt;statistics&lt;/li&gt;
&lt;li&gt;what's left in the old rails app (business logic, workflow engine)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far I pretty much know what I'm doing. However I don't have much experience in the microservices world, so &lt;strong&gt;I have a bunch of questions for you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What about user authentication at the API for the new UI? Are JSON web tokens the way to go?&lt;/li&gt;
&lt;li&gt;What about the database? Should I split it along the microservices?&lt;/li&gt;
&lt;li&gt;How do the microservices communicate with each other? I prefer a message bus over direct REST calls, so...?

&lt;ul&gt;
&lt;li&gt;RabbitMQ?&lt;/li&gt;
&lt;li&gt;Kafka?&lt;/li&gt;
&lt;li&gt;Nats?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Is it worth learning the whole Kubernetes stack? Or is docker plus docker-compose (or helm???) enough? Any other alternatives?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actually my monolith isn't even dockerized, yet. Currently I feel overwhelmed by all the technologies I can chose from. &lt;strong&gt;But what's the right technology stack for rails? And for me? This rails application is only a hobby project. Any recommendations?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>microservices</category>
      <category>kubernetes</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Automatic Termination of Closed Source Licenses</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Tue, 15 Oct 2019 23:06:36 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/automatic-termination-of-closed-source-licenses-4436</link>
      <guid>https://forem.com/thorstenhirsch/automatic-termination-of-closed-source-licenses-4436</guid>
      <description>&lt;p&gt;Let's slip into the role of your customer...&lt;/p&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;So you've finally found an app for your business in the app store. It's being developed by an aspiring developer who somehow happens to know exactly what you need. Sure, the app has some rough edges, but after you give him some feedback it gets better and better. You decide to buy the pro version with an annual fee, and you integrate all your workflows in the app. Everything's great!&lt;/p&gt;

&lt;p&gt;Some years later your business relies completely on this app, but suddenly you receive a letter from the developer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dear customers,&lt;/p&gt;

&lt;p&gt;as you might have noticed, there have been no updates for the app in a while. The reason is that I've lost interest, so I've decided to abandon it.&lt;/p&gt;

&lt;p&gt;So long, and thank you for the fish!&lt;br&gt;
Your aspiring developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh shit! You immediately contact him in order to convince him to continue supporting your beloved app. You even throw cash at him, but to no avail. He says he just landed a job at Google, something with AI, and has no interest anymore to solve your worldly problems.&lt;/p&gt;

&lt;p&gt;You keep using the (now unsupported) app, but after upgrading your Mac to Catalina it doesn't work anymore. You're afraid you have to close your business.&lt;/p&gt;

&lt;h1&gt;
  
  
  Open Source to the Rescue!
&lt;/h1&gt;

&lt;p&gt;Enough role-play. Maybe some of us can relate to the developer in this example and say that this customer was crazy to build his business on an app from a single developer. Or maybe some of us think that the developer acts irresponsible and selfish.&lt;/p&gt;

&lt;p&gt;We don't have to pick sides, we just have to accept that situations like these happen and useful or even important software gets abandoned. These situations can easily be resolved if the app was open source - the customer can just pay some other developer to migrate the app on Catalina. But not every developer wants to publish his apps under an open source license. Monetarization might indeed be a problem if you cannot rely on making money with support and services alone. But whatever the reasons might be - the developer is the author, and copyright law grants him to choose whatever license he wants. Then the software lives with its license happily ever after. End of the story.&lt;/p&gt;

&lt;p&gt;...or maybe not?&lt;/p&gt;

&lt;h1&gt;
  
  
  Big Business (Contract Law)
&lt;/h1&gt;

&lt;p&gt;The company I work for has licensed a lot of closed source software. Our lawyers address the risk of losing software support with a paragraph like the following one, which is put in all software vendor contracts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In case of bankruptcy the vendor must provide the source code of the licensed applications in complete and compilable form.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That seems to be a reasonable solution. However I'm not sure if my employer can really exercise this claim. The software might be composed of different parts, for which the vendor might not have the rights to give others the source code.&lt;/p&gt;

&lt;p&gt;Anyhow - here we see a customer's effort to get control over the software's source code when the vendor cannot support it anymore.&lt;/p&gt;

&lt;h1&gt;
  
  
  Comparison to Copyright in Music
&lt;/h1&gt;

&lt;p&gt;Musicians are paid royalties for the songs they've written or performed. This is a perpetual revenue stream that can (and should) last the whole life of an artist in order to prevent him from poverty. In many countries  royalties even continue after the author's death - 50 year, 70 years, maybe even 95 years after his death and are being paid to his descendants. But then the exclusive rights of the author ends and the song enters public domain, which is pretty similar to open source.&lt;/p&gt;

&lt;p&gt;So music might be timeless, but copyright enforces "open source" after quite some time. The time of course is very, very long, but music is timeless. Software is not.&lt;/p&gt;

&lt;h1&gt;
  
  
  Enforcing Open Source by Law
&lt;/h1&gt;

&lt;p&gt;I think it's time for a law that enforces the end of a closed source license - a law that automatically enforces its release under an open source license for circumstances that factually end support, e.g. when a software developer loses interest, when a software vendor goes bankrupt, or when a (single) developer dies.&lt;/p&gt;

&lt;p&gt;What do you think of such a law?&lt;/p&gt;

&lt;p&gt;P.S.: In my article the term "open source" is used synonymously for free software.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>startup</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Poll: Do You Know What "Idempotent" Means?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Thu, 27 Dec 2018 13:23:51 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/poll-do-you-know-what-idempotent-means-4759</link>
      <guid>https://forem.com/thorstenhirsch/poll-do-you-know-what-idempotent-means-4759</guid>
      <description>&lt;p&gt;In a very good &lt;a href="https://fahrplan.events.ccc.de/congress/2018/Fahrplan/events/9607.html"&gt;talk about TLS 1.3&lt;/a&gt; at &lt;a href="https://events.ccc.de/congress/2018/wiki/index.php/Main_Page"&gt;35c3&lt;/a&gt; Hanno Böck wondered if web developers know what "idempotent" means. Now before you fall into a rage, Hanno was not mean, but rather funny, so let's take his talk as an opportunity to talk about idempotence in web applications. I'm really curious, so let's (mis-) use dev.to's like system as a poll:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you know what "idempotent" means or&lt;/strong&gt; - after checking out &lt;a href="https://en.wikipedia.org/wiki/Idempotence"&gt;wikipedia&lt;/a&gt; - &lt;strong&gt;used the concept in your projects?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes idempotent requests to the server are required and obvious, but other times it might be tricky ...or not required at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dev.to&lt;/strong&gt;'s like buttons don't seem to be idempotent at first, because clicking once activates the like, clicking twice deactivates the like. You might think that the request is just a toggle. But it's not. Each request contains an action ("create" or "destroy"). So the 1st click produces:

&lt;code&gt;{"result": "create", "category": "like"}&lt;/code&gt;

. And this request &lt;strong&gt;is idempotent&lt;/strong&gt;, because if something goes wrong on the network layer and the server receives this request twice it will still do the right thing, it will activate the like on the 1st request and do nothing on the 2nd request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium&lt;/strong&gt;'s clapping system is &lt;strong&gt;not idempotent&lt;/strong&gt;. A request looks like this:

&lt;code&gt;{"userId":"my123id456","clapIncrement":1}&lt;/code&gt;

. And you can also see that it's not idempotent, because clapping once turns the counter from $old-value to $old-value + 1, while clapping twice turns it to $old-value + 2. But in this case it's not a bug, you are explicitly allowed to clap multiple times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Please vote&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;heart/like = yes&lt;/strong&gt;, I already knew what "idempotent" means or used the concept in past projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;unicorn = no&lt;/strong&gt;, this is new to me or I've seen no need to implement this concept in my projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I for myself must admit, that I've learned the term only 2 or 3 years ago. However I knew its concept and have used it for more than 10 years in past projects. I guess the poll is pretty anonymous (maybe with the exception of the dev.to admins), so please vote with honesty. &lt;/p&gt;

&lt;p&gt;By the way: 35c3 is one of the largest hacker events in the world and the talks are streamed &lt;a href="https://media.ccc.de"&gt;live&lt;/a&gt;. You will also find recordings of the talks later behind this link.&lt;/p&gt;

&lt;p&gt;// cover photo  by Yves Sorge / CC BY&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Do You Know Mental Models in IT?</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sun, 17 Jun 2018 16:29:04 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/do-you-know-mental-models-in-it-2128</link>
      <guid>https://forem.com/thorstenhirsch/do-you-know-mental-models-in-it-2128</guid>
      <description>&lt;p&gt;It's been 17 years since I started working in my 1st real job. It was in a bank and it was introduction week. There were 50 other students in the same situation as I and we were all sitting in an awe inspiring room full of ornaments. We were waiting for the next speaker. An elderly man entered the stage. The experience of decades could be seen in the wrinkles in his face. And I will never forget what he told us:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No matter what kind of business department you will work in, always remember that it's all about ledgers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So this old banker's mental model was the ledger. Actually he was using the &lt;a href="https://www.accountingtools.com/articles/what-is-a-t-account.html"&gt;T account representation&lt;/a&gt; of a ledger in his presentation. And he explained, that no matter what kind of financial business he was working on, he always transferred it on ledgers in his mind. Retail credit, cash transfer, trade finance, lending, money market, funding, ... you name it. By applying all problems on ledgers he could easily understand the business of each and every department in the bank.&lt;/p&gt;

&lt;h1&gt;
  
  
  Are there Mental Models in IT?
&lt;/h1&gt;

&lt;p&gt;After reading &lt;a href="https://jamesclear.com/feynman-mental-models"&gt;James Clear's article about mental models (and Richard Feynman)&lt;/a&gt; I asked myself what mental models we have in IT. What abstractions can we use to reason about any kind of problem we're confronted with? Whenever I reason about a new system I instantly think about its data modelling, so I guess these 2 might qualify as mental models in IT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity Relationships (ER)&lt;/li&gt;
&lt;li&gt;Object Oriented Programming (OOP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are design patterns a mental model? I don't think so. Each pattern suits a special problem, so a pattern is by far not generic enough to work as a mental model. And what about the sum of all patterns? No, I think trying to find patterns is just human behaviour.&lt;/p&gt;

&lt;p&gt;What about Functional Programming (FP)? Since I'm not proficient in FP I don't know if it can work as a mental model. Does it?&lt;/p&gt;

</description>
      <category>ledger</category>
      <category>finance</category>
      <category>banks</category>
    </item>
    <item>
      <title>Ethereum Mainnet &amp; Kovan Nodes on Same Machine</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sun, 31 Dec 2017 13:32:56 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/ethereum-mainnet--kovan-nodes-on-same-machine-369l</link>
      <guid>https://forem.com/thorstenhirsch/ethereum-mainnet--kovan-nodes-on-same-machine-369l</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Right after installing an Ethereum node on my developer machine, the initial sync caused it to run wild, pushing the fan to its limit, and in the end I had several gigabytes of blockchain data on my tiny SSD. Sounds familiar? Then this guide is for you.&lt;/p&gt;

&lt;p&gt;You don't need to run the node on your development machine directly, any computer in your network can do the job. So I've chosen my NAS, which is running Ubuntu 17.10 and has enough space on its HDD.&lt;/p&gt;

&lt;p&gt;And since we're developers, it is a good idea not only to have access to the mainnet, but also to one of the test nets. So I will setup an additional parity instance for the kovan chain. &lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;Please tell me that I was too dumb to find a PPA repo for &lt;a href="http://parity.io"&gt;parity&lt;/a&gt; yesterday. I cannot believe that it doesn't exist. Anyway, the parity installation instructions tell you to download a .deb file, so here we go with the beta release, which is 1.8.x at the time of writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="s2"&gt;"https://d1h4xl4cr1h0mo.cloudfront.net/beta-release/x86_64-unknown-linux-gnu/parity"&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; parity.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; parity.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a download option with curl/bash script, but since you never know what it does I prefer to download and install a .deb file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Ethereum Setup
&lt;/h1&gt;

&lt;p&gt;We will need an Ethereum account in each instance for interacting with contracts and such. I don't want these accounts (and their keys) in a system directory, since they are probably coupled to my user (I will refer to him as &lt;em&gt;hans&lt;/em&gt; in this article). I also want to keep open the option of adding more instances that might use accounts of other users. So let's create some directories in my user's home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/ethereum
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/ethereum/mainnet
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/ethereum/kovan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And wow it's time to create an account in each chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;parity account new &lt;span class="nt"&gt;-d&lt;/span&gt; ~/ethereum/mainnet
parity account new &lt;span class="nt"&gt;-d&lt;/span&gt; ~/ethereum/kovan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to import existing accounts instead, you can follow the instruction in the official &lt;a href="https://github.com/ethereum/go-ethereum/wiki/Managing-your-accounts"&gt;wiki&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  System Setup
&lt;/h1&gt;

&lt;p&gt;Let's start with the directory for our config files and our log files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /etc/ethereum
&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /var/log/parity-mainnet.log
&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /var/log/parity-kovan.log
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;hans /var/log/parity-&lt;span class="k"&gt;*&lt;/span&gt;.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now put the config files for our chains into /etc/systemd. You will need sudo permissions for this file and the 3 other files in this paragraph to come. Let's start with &lt;strong&gt;/etc/ethereum/config-mainnet.toml&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[parity]
chain = "mainnet"
base_path = "/home/hans/ethereum/mainnet"
identity = ""

[account]
unlock = ["0x6e6dbb24dd98dd37c41a9964a766e5eb64352d9c"]
password = ["/home/hans/ethereum/mainnet/password"]

[ui]
disable = false
port = 8180
interface = "all"

[network]
port = 30303
reserved_only = false
#reserved_peers = "/etc/parity/mainnetpeers.txt"

[rpc]
disable = false
port = 8545
interface = "all"
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]

[websockets]
disable = false
port = 8546
interface = "all"
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]

[ipc]
disable = false
apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"]

[dapps]
disable = false

[secretstore]
http_interface = "local"
http_port = 8082
interface = "local"
port = 8083

[ipfs]
enable = false
port = 5001
interface = "all"

[misc]
log_file = "/var/log/parity-mainnet.log"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's &lt;strong&gt;/etc/ethereum/config-kovan.toml&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[parity]
chain = "kovan"
base_path = "/home/hans/ethereum/kovan"
identity = ""

[account]
unlock = ["0xaa125f607bb23f41c0c45c27eca820f2b133d1b6"]
password = ["/home/hans/ethereum/kovan/password"]

[ui]
disable = false
port = 18180
interface = "all"

[network]
port = 30304
reserved_only = false
#reserved_peers = "/etc/parity/kovanpeers.txt"

[rpc]
disable = false
port = 18545
interface = "all"
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]

[websockets]
disable = false
port = 18546
interface = "all"
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]

[ipc]
disable = false
apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"]

[dapps]
disable = false

[secretstore]
http_interface = "local"
http_port = 18082
interface = "local"
port = 18083

[ipfs]
enable = false
port = 15001
interface = "all"

[misc]
log_file = "/var/log/parity-kovan.log"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need a systemd config file for each instance, so that the system will start parity at boot time (but don't worry, the processes will run under your user account). Let's start with mainnet again, the file name is &lt;strong&gt;/etc/systemd/system/parity-mainnet.service&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Parity Mainnet Daemon
After=network.target

[Service]
# run as root, set base_path in config.toml
ExecStart=/usr/bin/parity --config /etc/parity/config-mainnet.toml
# To run as user, comment out above and uncomment below, fill in user and group
# picks up users default config.toml in $HOME/.local/share/io.parity.ethereum/
User=hans
# Group=groupname
# ExecStart=/usr/bin/parity
Restart=on-failure
Nice=15

# Specifies which signal to use when killing a service. Defaults to SIGTERM.
# SIGHUP gives parity time to exit cleanly before SIGKILL (default 90s)
KillSignal=SIGHUP

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here comes &lt;strong&gt;/etc/systemd/system/parity-kovan.service&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Parity Kovan Daemon
After=network.target

[Service]
# run as root, set base_path in config.toml
ExecStart=/usr/bin/parity --config /etc/parity/config-kovan.toml
# To run as user, comment out above and uncomment below, fill in user and group
# picks up users default config.toml in $HOME/.local/share/io.parity.ethereum/
User=hans
# Group=groupname
# ExecStart=/usr/bin/parity
Restart=on-failure
Nice=15

# Specifies which signal to use when killing a service. Defaults to SIGTERM.
# SIGHUP gives parity time to exit cleanly before SIGKILL (default 90s)
KillSignal=SIGHUP

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check if systemd has found your new services with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl list-units parity&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Test Run
&lt;/h1&gt;

&lt;p&gt;We're ready for a test ride. Just start an instance in your shell (as your user) with one of the config files we just created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;parity &lt;span class="nt"&gt;--config&lt;/span&gt; /etc/parity/config-mainnet.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see some information and after some seconds the sync process will begin, printing a new status line every few seconds. If everything's fine press Ctrl+c to stop the test run.&lt;/p&gt;

&lt;h1&gt;
  
  
  Starting &amp;amp; Enabling the Service
&lt;/h1&gt;

&lt;p&gt;First we start the services, then we enable autostart at boot time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start parity-mainnet
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start parity-kovan
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;parity-mainnet
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;parity-kovan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went fine, you will probably see your shell becoming less responsive. I tried to minimise this effect by giving the parity processes a low priority by setting the nice level to 15. My suggestion for the nice level in general is: since you always want the system to be responsive, never give your user processes a higher priority (i.e. a negative number). A nice level of 15 is pretty low. It can go down as low as 20. However setting the nice level to 20 might not help much, because the load that is caused by the initial (warp) sync of the chains spawns quite a lot of threads. The load on my dual core machine goes up to 8 or 9 during this task. &lt;a href="https://github.com/paritytech/parity/issues/4940"&gt;Here&lt;/a&gt; is a more detailed explanation of that problem, which is being worked on, but Parity 1.8.x still comes with RocksDB, so for now we have to live with this load and a decreased responsiveness of the shell. It will get better after the initial sync is over.&lt;/p&gt;

&lt;h1&gt;
  
  
  Port Info
&lt;/h1&gt;

&lt;p&gt;You might wonder about the ports I've chosen. Here's my reasoning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30303 is the default ethereum port, so mainnet will use it&lt;/li&gt;
&lt;li&gt;30304 is not used by any other module, so kovan will use it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rpc and http ports (8xxx) can not follow the same logic, because some of the default values are consecutive. So here we will use...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8xxx for mainnet (8180 for ui, 8545 for rpc, 8546 for websockets, ...)&lt;/li&gt;
&lt;li&gt;18xxx for kovan (18180 for ui, 18545 for rpc, 18546 for websockets, ...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;UPDATE 2018-01-20&lt;/strong&gt;&lt;br&gt;
systemd: WantedBy=multi-user.target instead of default.target&lt;/p&gt;

</description>
      <category>ethereum</category>
      <category>kovan</category>
      <category>parity</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Building React Components with Bootstrap Studio</title>
      <dc:creator>Thorsten Hirsch</dc:creator>
      <pubDate>Sun, 12 Nov 2017 01:10:21 +0000</pubDate>
      <link>https://forem.com/thorstenhirsch/building-react-components-with-bootstrap-studio-a19</link>
      <guid>https://forem.com/thorstenhirsch/building-react-components-with-bootstrap-studio-a19</guid>
      <description>&lt;p&gt;Do you know &lt;a href="https://bootstrapstudio.io" rel="noopener noreferrer"&gt;Bootstrap Studio&lt;/a&gt;? It's a graphical HTML/Bootstrap builder. The latest version features support for Bootstrap 4 and includes a migration function for converting Bootstrap 3 projects to Bootstrap 4. It's not free, but at least the price tag is not very high. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwu2bua97fgapsmihbayu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwu2bua97fgapsmihbayu.png" alt="Screenshot of Bootstrap Studio 4" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I liked the idea of using it for building my Rails web application, I was wondering if there's a better way to use it than manually copy-and-pasting HTML and CSS code from BSS's export. Spoiler alert: &lt;strong&gt;Yes, there is!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I recently integrated React into my Rails app and that was just about the right decision, because a company called WIX has written &lt;a href="https://github.com/wix/react-templates" rel="noopener noreferrer"&gt;react-templates&lt;/a&gt;, which provides a way to wrap a BSS export into a React component. &lt;strong&gt;So the idea is to export the BSS page to html, transform it to a React template, which is a Javascript function, and use that function as React's render function in a component.&lt;/strong&gt; Since BSS can run a custom export script each time I hit the export button, this process can be automated.&lt;/p&gt;

&lt;p&gt;Here's a step-by-step guide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add react-rails to your rails application, see &lt;a href="https://github.com/reactjs/react-rails" rel="noopener noreferrer"&gt;github page&lt;/a&gt; for details.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an additional directory &lt;code&gt;components_rt&lt;/code&gt; under &lt;code&gt;app/assets/javascripts&lt;/code&gt; of your rails application for the template files. You also need to add an import line for it right before the import line of the components directory in &lt;code&gt;app/assets/javascripts/application.js.coffee&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#= require_tree ./components_rt
#= require_tree ./components
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the rt command from react-templates:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-templates &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In BSS you can supply an export script. Use this one and change the TARGET to your needs:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/path/to/rails/app/assets/javascripts/components_rt
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;          &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"argument error: bss export directory required"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"bss export directory does not exist"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"target does not exist: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="c"&gt;# is rt installed?&lt;/span&gt;
which rt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;       &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"rt (react-template compiler) is not installed"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="c"&gt;# main&lt;/span&gt;
&lt;span class="nv"&gt;RC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.html&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.html&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.rt"&lt;/span&gt;
    xmllint &lt;span class="nt"&gt;--html&lt;/span&gt; &lt;span class="nt"&gt;--xpath&lt;/span&gt; &lt;span class="s1"&gt;'/html/body/*'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|&amp;lt;script .*script&amp;gt;||g'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|%7B|{|g'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# fix due to xmllint/xpath bug 788398&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|%7D|}|g'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# fix due to xmllint/xpath bug 788398&lt;/span&gt;
    &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
    rt &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;RC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$RC&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$RC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write a React component, let's call it "Hello":&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;helloRT&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you can build your component in BSS. Rename the html file to "hello.html". It will be transformed into the Javascript function "helloRT" when you click on export.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the component in any view of your rails project:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= react_component 'Hello' %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put your elements under /html/body in BSS. There's only 1 element allowed directly under /html/body, so wrap your stuff in a container element. And use 1 HTML file in BSS for each React component.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

&lt;p&gt;P.S.: You might want to extend the export script by the ability to bring CSS files from the BSS export into Rails.&lt;/p&gt;

</description>
      <category>react</category>
      <category>bootstrap</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
  </channel>
</rss>
