<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xml" href="/feed.xslt.xml"?><feed xmlns="http://www.w3.org/2005/Atom"><generator uri="http://jekyllrb.com" version="3.2.1">Jekyll</generator><link href="https://zolmeister.com/feeds/posts/default" rel="self" type="application/atom+xml" /><link href="https://zolmeister.com/" rel="alternate" type="text/html" /><updated>2016-09-26T13:02:29-05:00</updated><id>https://zolmeister.com/</id><title>Zolmeister</title><subtitle>Zolmeister - Blog</subtitle><author><name>Zoli Kahan</name></author><entry><title>JWT Authentication</title><link href="https://zolmeister.com/2016/09/jwt-auth.html" rel="alternate" type="text/html" title="JWT Authentication" /><published>2016-09-26T00:00:00-05:00</published><updated>2016-09-26T00:00:00-05:00</updated><id>https://zolmeister.com/2016/09/jwt-auth</id><content type="html" xml:base="https://zolmeister.com/2016/09/jwt-auth.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/jwt_logo.png&quot; alt=&quot;json web tokens&quot;&gt;&lt;/p&gt;

&lt;p&gt;Authentication models for hosting platforms require more considerations due to 3rd party api access.
We wanted to be able to issue user auth tokens for 3rd party apps (think OAuth), as well as be able to verify authentication of local native api calls (through our iframe native app hooks).
Our solution was to use Json Web Tokens (JWT) with ES256 encryption (public/private key pair), which allows us to verify signed tokens using a public key which can be shared without compromising the security of our tokens. It also allows us to statelessly sign tokens for 3rd party developer use.&lt;/p&gt;

&lt;p&gt;Here&amp;#39;s a sample implementation of a basic authentication strategy (extensible to allow scoped tokens).&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# auth_model.coffee&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;jwt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'jsonwebtoken'&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;fromUserId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;accessToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sign&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JWT_ES256_PRIVATE_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'ES256'&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JWT_ISSUER&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;userIdFromAccessToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;promisify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JWT_ES256_PUBLIC_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JWT_ISSUER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the corresponding Express middleware:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# auth_middleware.coffee&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;accessToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accessToken&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;accessToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;Auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;userIdFromAccessToken&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;accessToken&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getById&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Authentication successful&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>MySQL Migrations</title><link href="https://zolmeister.com/2016/08/mysql-migrations.html" rel="alternate" type="text/html" title="MySQL Migrations" /><published>2016-08-27T00:00:00-05:00</published><updated>2016-08-27T00:00:00-05:00</updated><id>https://zolmeister.com/2016/08/mysql-migrations</id><content type="html" xml:base="https://zolmeister.com/2016/08/mysql-migrations.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/mysql-logo.jpg&quot; alt=&quot;MySQL logo&quot;&gt;&lt;/p&gt;

&lt;h3&gt;Migrating 17M rows with no downtime - Isotope&lt;/h3&gt;

&lt;p&gt;Scenario: 17M rows in a table, constant writes/reads, slave/master setup.&lt;/p&gt;

&lt;p&gt;If you  try to run an ALTER TABLE (especially on older versions of MySQL), most operations will lock the table for writes for the entire run-time of the query. To avoid this, we need to incrementally update the table, incorporating write changes as they arrive.&lt;/p&gt;

&lt;p&gt;Our solution was to use the &lt;a href=&quot;https://github.com/soundcloud/lhm&quot;&gt;Large Hadron Migrator&lt;/a&gt;, built and open-sourced by SoundCloud. LHM creates a copy table for the migration, with change triggers set up to keep up with new writes. It handles all the low-level details of the migration, and generally works well (though slowly, as I&amp;#39;ll illustrate later).&lt;/p&gt;

&lt;p&gt;First step is to create a migration.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;apt-get -y install libmysqlclient-dev libmysqlclient18 ruby-dev build-essential rake
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;gem install activerecord-mysql-adapter standalone_migrations lhm
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;rake db:new_migration &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;add_some_column_to_some_table
run/test locally: &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production rake db:migrate&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
dump schema: &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production rake db:schema:dump&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our database ran too slowly for the default change-rate of LHM (batches were too large, causing writes to time-out). By decreasing the speed of migration we avoided this, at the cost of migrations taking &lt;strong&gt;20hr+&lt;/strong&gt; (we eventually moved away from MySQL).&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Throttle is in ms - default 100&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Stride is in number of rows - default 40,000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:stride&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:throttle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, we&amp;#39;ll build a docker container to be able to run the migration remotely&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;FROM ubuntu:14.04

RUN apt-get -y update
RUN apt-get -y install libmysqlclient-dev libmysqlclient18 ruby-dev build-essential rake
RUN gem install activerecord-mysql-adapter standalone_migrations lhm

ADD . /opt/isotope
WORKDIR /opt/isotope
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;docker build -t isotope .
docker push isotope
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, to run the migration itself&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;docker run -e RAILS_ENV=production -e MYSQL_MASTER_HOST=&amp;lt;MYSQL PRODUCTION IP&amp;gt; -e MYSQL_MASTER_PASS=&amp;lt;PASSWORD&amp;gt; -i -t isotope rake db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If something goes wrong during the migration we have to clean up the change table manually.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;drop `lhmn_*` tables
drop `lhmn_*` triggers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>Clay.io - A Reflection</title><link href="https://zolmeister.com/2016/05/clay-io.html" rel="alternate" type="text/html" title="Clay.io - A Reflection" /><published>2016-05-04T00:00:00-05:00</published><updated>2016-05-04T00:00:00-05:00</updated><id>https://zolmeister.com/2016/05/clay-io</id><content type="html" xml:base="https://zolmeister.com/2016/05/clay-io.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/clay_logo_728.png&quot; alt=&quot;Clay Games&quot;&gt;&lt;/p&gt;

&lt;h3&gt;Moving On&lt;/h3&gt;

&lt;p&gt;As of January I am no longer with Clay.
I&amp;#39;ve learned so much over the past year and a half, more so than I could have ever imagined.
Every minute working on Clay was a joy, and without a doubt one of the most fulfilling experiences of my life.
As for my next adventure, stay tuned.&lt;/p&gt;

&lt;h4&gt;Lesson 1: Focus&lt;/h4&gt;

&lt;p&gt;Focus proved to be the most important lesson I&amp;#39;ve learned about running a company.
It&amp;#39;s so easy to get distracted and off track, without even realizing it.
Focus means having concrete goals with timelines, and putting all major efforts towards those goals.
Concrete goals are measurable and achievable, e.g. &amp;quot;Improve Day 1 retention by 1% in 1 month&amp;quot;.
Goals without a timeline (to measure ROI by time) or that aren&amp;#39;t easily measurable (like happiness of users) should not be used.&lt;/p&gt;

&lt;p&gt;At Clay we picked &amp;quot;Engaged Game Plays&amp;quot; (game plays lasting at least 1min) as our first KPI (key performance indicator). We made sure that all work being done would have a measurable impact on this metric. Had we been even stricter about only doing necessary work (e.g. we still did user support) I think we would have been even better off.&lt;/p&gt;

&lt;p&gt;For most early stage companies I recommend retention (e.g. Day 1, Day 7) as the only key metric, because it&amp;#39;s easy to measure, test, and is a relatively clear indicator of value. I also recommend that everyone at the company know the metric, and understand that their work is to align with that goal. Constant reminders (e.g. posters, screens, etc.) can go a long way to getting everyone on the same page.&lt;/p&gt;

&lt;style&gt;
.clay_icons {
  display: flex;
  justify-content: space-between;
  padding-bottom: 40px;
}
&lt;/style&gt;

&lt;div class=&quot;clay_icons&quot;&gt;
  &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clay.clay&quot;&gt;
    &lt;img src=&quot;/assets/images/clay_icon.png&quot; alt=&quot;Clay Games&quot;&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clay.zop&quot;&gt;
    &lt;img src=&quot;/assets/images/zop_icon.png&quot; alt=&quot;Zop&quot;&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clay.doodledraw&quot;&gt;
    &lt;img src=&quot;/assets/images/clay_doodle_draw.png&quot; alt=&quot;Doodle Draw&quot;&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clay.kittencards&quot;&gt;
    &lt;img src=&quot;/assets/images/clay_kitten_cards.png&quot; alt=&quot;Kitten Cards&quot;&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.clay.memeblast&quot;&gt;
    &lt;img src=&quot;/assets/images/clay_meme_blast.png&quot; alt=&quot;Meme Blast&quot;&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;h4&gt;Lesson 2: Validation&lt;/h4&gt;

&lt;p&gt;Failure should always be an option. Just because something &lt;em&gt;should&lt;/em&gt; work, doesn&amp;#39;t mean that it &lt;em&gt;will&lt;/em&gt;. Any and all assumptions made about the problems you&amp;#39;re solving need to be validated, and the process this takes is imperative to the growth of the company.
This is where I think we could have improved the most.&lt;/p&gt;

&lt;p&gt;Being explicit about the assumptions of the company vision, e.g. &amp;quot;Users would rather do X than Y&amp;quot;, and validating as quickly as possible can save months of time and money (burn rate). This validation process can be difficult, complex, and/or expensive (to do properly), but how founders approach this problem can greatly improve chances of long-term company success.&lt;/p&gt;

&lt;h4&gt;Lesson 3: Science&lt;/h4&gt;

&lt;p&gt;Science was probably our biggest strength at Clay. We never really argued about anything; discussions were always focused around evidence-based metric growth. Even then nobody was ever &amp;quot;right&amp;quot; or &amp;quot;wrong&amp;quot; because we tried to A/B test all valid (high ROI) ideas. Science is all about proving (within statistical margins of error) assumptions correct or incorrect. We did this with nearly every feature we ever released, to both prove positive KPI impact and to calculate growth.&lt;/p&gt;

&lt;p&gt;We ended up building our own A/B testing analytics framework (&lt;a href=&quot;https://github.com/claydotio/hyperplane&quot;&gt;open-source&lt;/a&gt;) due to limitations (and cost) of other solutions. This was probably the hardest problem I solved at Clay, and even then I would like to take a second pass at it. I will likely write a blog post about it later this year.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/clay_mascot.png&quot; alt=&quot;Clay Mascot&quot;&gt;&lt;/p&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>Zorium v1.0.0 (╯°□°)╯︵ ┻━┻</title><link href="https://zolmeister.com/2015/05/zorium.html" rel="alternate" type="text/html" title="Zorium v1.0.0 (╯°□°)╯︵ ┻━┻" /><published>2015-05-24T00:00:00-05:00</published><updated>2015-05-24T00:00:00-05:00</updated><id>https://zolmeister.com/2015/05/zorium</id><content type="html" xml:base="https://zolmeister.com/2015/05/zorium.html">&lt;p&gt;&lt;a href=&quot;https://zorium.org/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/zorium_logo_728.png&quot; alt=&quot;zorium&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;The CoffeeScript Web Framework&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://zorium.org/&quot;&gt;Zorium&lt;/a&gt; (&lt;a href=&quot;https://github.com/Zorium/zorium&quot;&gt;source&lt;/a&gt;) is the web framework we use at &lt;a href=&quot;http://clay.io/&quot;&gt;Clay.io&lt;/a&gt;.&lt;br&gt;
Today it has finally reached version v1.0.0 (╯°□°)╯︵ ┻━┻  &lt;/p&gt;

&lt;p&gt;This has been one of the most challenging projects I&amp;#39;ve worked on, with many great lessons learned.
What follows is how Zorium came to be what it is today, and an explanation of design decisions in the framework.&lt;/p&gt;

&lt;p&gt;With Zorium (or any framework), the code is the easy part. The hard part is the design and architecture of an application.
This is where Zorium stands out from the rest. Just start with &lt;a href=&quot;https://github.com/Zorium/zorium-seed&quot;&gt;Zorium Seed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the full documentation: &lt;a href=&quot;https://zorium.org&quot;&gt;https://zorium.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href=&quot;https://github.com/Matt-Esch&quot;&gt;Matt Esch&lt;/a&gt; and &lt;a href=&quot;https://github.com/Raynos&quot;&gt;Jake Verbaten&lt;/a&gt;, without whom Zorium would not exist.&lt;/p&gt;

&lt;h4&gt;Example&lt;/h4&gt;

&lt;p&gt;Because every framework should start with code:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'zorium'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppComponent&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Zorium'&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'div.zorium'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'p.text'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;The Future is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'div'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &amp;lt;div class=&quot;zorium&quot;&amp;gt;&amp;lt;p class=&quot;text&quot;&amp;gt;The Future is Zorium&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components are just CoffeeScript classes&lt;/li&gt;
&lt;li&gt;There is no magic pre-processor (i.e. JSX)&lt;/li&gt;
&lt;li&gt;It&amp;#39;s simple, intuitive, and idiomatic. No magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;API Overview&lt;/h4&gt;

&lt;p&gt;The two most important methods in Zorium are &lt;code&gt;z()&lt;/code&gt; and &lt;code&gt;z.state()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;z()&lt;/code&gt; is simply an extension of &lt;a href=&quot;https://github.com/Matt-Esch/virtual-dom/blob/master/virtual-hyperscript/README.md&quot;&gt;virtual-hyperscript&lt;/a&gt;, which understands Zorium components.
The most difficult implementation detail here was constructing an in-memory cached tree for efficient rendering.
Eventually this was solved with &lt;a href=&quot;https://github.com/Matt-Esch/virtual-dom/blob/master/docs/thunk.md&quot;&gt;Thunks&lt;/a&gt; and traversing the virtual-tree (&lt;a href=&quot;https://github.com/Zorium/zorium/blob/master/src/z.coffee#L55&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Component composition example&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Brick&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.z-brick'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;Hello &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;House&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$brick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Brick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.z-house'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$brick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Zorium'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;z.state()&lt;/code&gt; creates an instance of &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md&quot;&gt;Rx.BehaviorSubject()&lt;/a&gt;,
with a &lt;code&gt;set()&lt;/code&gt; method for diffing the current value (and also some lazy-ness to make async more efficient).&lt;/p&gt;

&lt;p&gt;Originally a simpler model using &lt;a href=&quot;https://github.com/Raynos/observ-struct&quot;&gt;observ-struct&lt;/a&gt; was used, however it did not provide a declarative syntax for dealing with complex streams of data.  &lt;/p&gt;

&lt;p&gt;e.g. We want to display the number of likes of the users favorite game.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user changes their favorite game, the value should update.&lt;/li&gt;
&lt;li&gt;If another user likes the game, the value should update.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Note that we assume the models update themselves and emit streams.&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;numLikes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getMe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;flatMapLatest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;favoriteGameId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getById&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;favoriteGameId&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;numLikes&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Zorium Paper&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://zorium.org/paper&quot;&gt;https://zorium.org/paper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Zorium/zorium-paper&quot;&gt;Zorium Paper&lt;/a&gt; was the first npm-installable set of components for Zorium. Here&amp;#39;s what it looks like to use it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# npm install zorium-paper&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'zorium-paper/button'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Clicker&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.z-clicker'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;$content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Click Me'&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;isRaised&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key to using an npm module and maintaining efficient css (separable styles for production) is to use &lt;a href=&quot;http://webpack.github.io/&quot;&gt;webpack&lt;/a&gt;.
Webpack lets Zorium components declaratively define styles which can be efficiently loaded using webpack without resorting to inefficient inlining or other duplication.&lt;/p&gt;

&lt;p&gt;Just having webpack is not enough though. Without shadow-dom we don&amp;#39;t have true CSS isolation. In order to let developers use  semantic classes without conflicting with each other, a namespace pattern was developed:&lt;/p&gt;

&lt;p&gt;(&lt;a href=&quot;https://zorium.org/best-practices/stylus&quot;&gt;full docs&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'./index.styl'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BigDrawer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.z-big-drawer'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# namespace&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.blue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'blue'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// index.styl
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// namespace
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.z-big-drawer&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// direct children only
&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.blue&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;blue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;User-land components should use the &lt;code&gt;z-&lt;/code&gt; prefix, while library authors should namespace under a different prefix.&lt;/p&gt;

&lt;h4&gt;Server Side Rendering&lt;/h4&gt;

&lt;p&gt;Efficient server-side rendering is non-trivial. It requires application code to be written statelessly, and thus needs to be considered from the start of a project.
In fact, the state issue was so elusive, it wasn&amp;#39;t until after Zorium v1.0.0-rc15 that I realized a &lt;a href=&quot;https://github.com/Zorium/zorium/commit/87fdfa82d0da7583a5473c57b4d1b1f6599019b1&quot;&gt;critical mistake&lt;/a&gt; I had made regarding concurrent requests.  &lt;/p&gt;

&lt;p&gt;After realizing my mistake, I decided to take a page from React, and implement a simpler &lt;code&gt;z.renderToString()&lt;/code&gt; (with async support).&lt;br&gt;
Note that the server simply acts as a pre-renderer. Do not write server-side business logic here.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# server-side&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;renderToString&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;lt;!DOCTYPE html&amp;gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;html&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;lt;!DOCTYPE html&amp;gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;html&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The built in router, &lt;code&gt;z.router&lt;/code&gt;, attempts to expose an express-like api. Note the difference in where &lt;code&gt;new App()&lt;/code&gt; is called, as client-side you want a stateful App for transitions.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sometimes it will be necessary to split code between node and client-side. Use &lt;code&gt;window?&lt;/code&gt; to check:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffee&quot; data-lang=&quot;coffee&quot;&gt;&lt;span class=&quot;nx&quot;&gt;Promise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Promise&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Avoid webpack include&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_Promise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'promiz'&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_Promise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Zorium&amp;#39;s boldest claim is probably that you can achieve nearly 100% code re-use for client and server-side code. Normally with other frameworks database requests are made and injected at different initialization points within the application.  &lt;/p&gt;

&lt;p&gt;This doesn&amp;#39;t scale well for complex nested structures and requires duplicate effort in marshalling data. Zorium uses lazy-states and a platform-agnostic request library to create a seamless transition from client-side code to server-side code.&lt;/p&gt;

&lt;p&gt;This area is probably the most complex because developers usually don&amp;#39;t have to worry about state in this way. (e.g. creating singletons client-side is fine, but server-side will cause elusive caching bugs)&lt;/p&gt;

&lt;h4&gt;Comparison to other virtual-dom libraries&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/zorium_icon_256.png&quot; alt=&quot;zorium icon&quot;&gt;&lt;/p&gt;

&lt;p&gt;The Zorium framework is a collection of patterns, which are expressed in &lt;a href=&quot;https://github.com/Zorium/zorium-seed&quot;&gt;Zorium Seed&lt;/a&gt;. However, as a library is has some key differences from other popular virtual-dom libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://facebook.github.io/react/&quot;&gt;React&lt;/a&gt; - The Facebook-backed library that started it all.&lt;br&gt;

&lt;ul&gt;
&lt;li&gt;React source-code is a convoluted mess. Extending it is nearly impossible. [1]&lt;br&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://facebook.github.io/flux/docs/overview.html&quot;&gt;Flux&lt;/a&gt; architecture is unnecessarily complex. [2]&lt;/li&gt;
&lt;li&gt;React still has some clear deficiencies with complex animations. [3]&lt;/li&gt;
&lt;li&gt;The React API makes questionable design choices. [4][5][6]&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lhorie.github.io/mithril/&quot;&gt;Mithril&lt;/a&gt; - A micro vritual-dom library which seemed quite promising.

&lt;ul&gt;
&lt;li&gt;We initially used this at Clay for a few months (in a fork), but eventually got rid of it entirely.&lt;/li&gt;
&lt;li&gt;Source-code is quite complicated, making extending difficult.&lt;/li&gt;
&lt;li&gt;Manual state-management and rendering updates hurt scalability as our projects grew.&lt;/li&gt;
&lt;li&gt;Lack of structured best-practices lead to a lot of guessing and implementation mistakes.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both React and Mithril use different virtual-dom implementations.
Zorium uses the &lt;a href=&quot;https://github.com/Matt-Esch/virtual-dom&quot;&gt;virtual-dom&lt;/a&gt; library, which is the standard behind many other virtual-dom frameworks (e.g. &lt;a href=&quot;https://github.com/Raynos/mercury&quot;&gt;Mercury&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;1: Seriously, &lt;a href=&quot;https://github.com/facebook/react/blob/52752446760dee0bc7232b4146f5a309ac57f065/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js&quot;&gt;this&lt;/a&gt; &lt;a href=&quot;https://github.com/facebook/react/blob/52752446760dee0bc7232b4146f5a309ac57f065/src/renderers/dom/client/ReactReconcileTransaction.js&quot;&gt;is&lt;/a&gt; &lt;a href=&quot;https://github.com/facebook/react/blob/52752446760dee0bc7232b4146f5a309ac57f065/src/renderers/shared/reconciler/ReactCompositeComponent.js&quot;&gt;incomprehensible&lt;/a&gt;. (vs &lt;a href=&quot;https://github.com/Zorium/zorium/blob/master/src/render.coffee&quot;&gt;z.render()&lt;/a&gt; for example)&lt;br&gt;
2: Because Zorium supports &lt;a href=&quot;https://github.com/Reactive-Extensions/RxJS&quot;&gt;RxJS&lt;/a&gt; Observables in state, async code is declaratively directional (and streaming) and thus avoids the data-binding issues Flux is said to address.&lt;br&gt;
3: Inter-component communication with animations is nearly impossible (without an event-bus and side-effects), whereas Zorium simply uses RxJS observables to handle complex communications in a declarative way.&lt;br&gt;
4: &lt;code&gt;React.createClass()&lt;/code&gt; (and extending React.Component) means non-idiomatic initialization. (vs. explicitly calling &lt;code&gt;new Component()&lt;/code&gt;)&lt;br&gt;
5: JSX magic is not idiomatic code.&lt;br&gt;
6: Mixins are also a mistake, they are opaque and imperative.  &lt;/p&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>10x: Service Discovery at Clay.io</title><link href="https://zolmeister.com/2015/02/10x-service-discovery-at-clay-io.html" rel="alternate" type="text/html" title="10x: Service Discovery at Clay.io" /><published>2015-02-17T00:00:00-06:00</published><updated>2015-02-17T00:00:00-06:00</updated><id>https://zolmeister.com/2015/02/10x-service-discovery-at-clay-io</id><content type="html" xml:base="https://zolmeister.com/2015/02/10x-service-discovery-at-clay-io.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/synapse.png&quot; alt=&quot;synapse architecture&quot;&gt;&lt;/p&gt;

&lt;h4&gt;Architecture&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Service-oriented_architecture&quot;&gt;Service Oriented Architectures&lt;/a&gt;
can be one of the most iterable and available software configurations for building almost any product.
There are many challenges that come with these systems, probably the biggest of which is service discovery.
This is how your services will communicate with each other. For our service discovery, we turn again to Docker. If you haven&amp;#39;t read how
we do deploys: &lt;a href=&quot;http://zolmeister.com/2014/12/10x-docker-at-clay-io.html&quot;&gt;Docker at Clay.io&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Tutorial&lt;/h2&gt;

&lt;p&gt;Synapse (&lt;a href=&quot;https://github.com/claydotio/synapse&quot;&gt;https://github.com/claydotio/synapse&lt;/a&gt;) is a daemon which dynamically configures a local HAproxy configuration which routes
requests to services within your cluster. It watches Amazon EC2 (or another endpoint) for services, specified in a configuration file.
We use the &lt;code&gt;ec2tag&lt;/code&gt; watcher, which easily lets us add servers to our cluster by tagging them.&lt;/p&gt;

&lt;h4&gt;HAProxy&lt;/h4&gt;

&lt;p&gt;Alright, so let&amp;#39;s start with the services. They will be talking to each other via the local
&lt;a href=&quot;http://www.haproxy.org/&quot;&gt;HAProxy&lt;/a&gt; instance. Because services run inside docker, we need to specify the host IP
for services to look for services at a specific port.&lt;/p&gt;

&lt;p&gt;Here, we use &lt;a href=&quot;http://www.ansible.com/home&quot;&gt;Ansible&lt;/a&gt; to pass the local IP and port to service running on that machine.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;SERVICE_API=http://{ ansible_default_ipv4.address }:{ service_port }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For a service to use another service, they simply make calls to that IP/port.
The key here is that the IP is the local machine IP, which is handled by HAProxy.
We have released an HAProxy docker container which watches a mounted config file,
and updates automatically on changes:&lt;br&gt;
&lt;a href=&quot;https://github.com/claydotio/haproxy&quot;&gt;https://github.com/claydotio/haproxy&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;docker run
    --restart always
    --name haproxy
    -v /var/log:/var/log
    -v /etc/haproxy:/etc/haproxy
    -d
    -p 50001:50001
    -p 50002:50002
    -p 50003:50003
    -p 50004:50004
    ...
    -p 1937:1937
    -t clay/haproxy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By default, we use the &lt;a href=&quot;https://github.com/claydotio/haproxy/blob/master/noop.cfg&quot;&gt;noop config&lt;/a&gt; at &lt;code&gt;/etc/haproxy&lt;/code&gt;, which gets mounted inside the docker container and watched for changes. We will be mounting the same haproxy config inside our synapse container in a moment. It&amp;#39;s important to note that if this container goes down, all services on the machine will be cut off from all other services. For that reason, we have allocated additional ports to the container for use with new services in the future (as they cannot be &lt;a href=&quot;http://stackoverflow.com/questions/19897743/exposing-a-port-on-a-live-docker-container&quot;&gt;dynamically allocated&lt;/a&gt;).&lt;/p&gt;

&lt;h4&gt;Synapse&lt;/h4&gt;

&lt;p&gt;Ok, now it&amp;#39;s time to actually set up synapse.&lt;/p&gt;

&lt;p&gt;Running synapse (thanks to our public Docker container) is easy.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;docker run
    --restart always
    --name synapse
    -v /var/log:/var/log
    -v /etc/synapse:/etc/synapse
    -v /etc/haproxy:/etc/haproxy
    -e AWS_ACCESS_KEY_ID=XXX
    -e AWS_SECRET_ACCESS_KEY=XXX
    -e AWS_REGION=XXX
    -d
    -t clay/synapse
    synapse -c /etc/synapse/synapse.conf.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice how we are mounting a synapse config, and an haproxy config inside the container. The HAProxy config is our noop config from before (because it will be auto-generated by synapse), but let&amp;#39;s look into configuring synapse.&lt;/p&gt;

&lt;p&gt;Configuring synapse can be a bit tricky, as the &lt;a href=&quot;https://github.com/claydotio/synapse&quot;&gt;documentation&lt;/a&gt; could be better. Here is an example config that should explain everything that&amp;#39;s missing in the docs:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/claydotio/synapse/blob/master/example.synapse.conf.json&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;services&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;myservice&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;discovery&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// use amazon ec2 tags&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ec2tag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;tag_name&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;servicename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;tag_value&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// if this is too low, Amazon will rate-limit and block requests&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;check_interval&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;120.0&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;haproxy&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This is the port other services will use to talk to this service&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// e.g. http://10.0.1.10:50003&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;listen&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;mode http&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This is the port that the service exposes itself&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;server_port_override&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;50001&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This is our custom (non-documented) config for our backup server&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// See http://zolmeister.com/2014/12/10x-docker-at-clay-io.html&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// for details on how our zero-downtime deploys work&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;server_backup_port&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;50002&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;server_options&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;check&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// See the manual for details on parameters:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// http://cbonte.github.io/haproxy-dconv/configuration-1.5.html&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;haproxy&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This is never used because HAProxy runs in a separate container&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Reloads happen automatically via the file-watcher&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;reload_command&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;echo noop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;config_file_path&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/haproxy/haproxy.cfg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;socket_file_path&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/haproxy/stats.sock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;do_writes&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;do_reloads&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;do_socket&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// By default, this is localhost, however because HAProxy is running&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// inside of a container, we need to expose it to the host machine&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;bind_address&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;global&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;daemon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;user    haproxy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;group   haproxy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;chroot  /var/lib/haproxy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;maxconn 4096&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;log     127.0.0.1 local0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;log     127.0.0.1 local1 notice&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;defaults&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;log            global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;mode           http&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;maxconn        2000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;retries        3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;timeout        connect 5s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;timeout        client  1m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;timeout        server  1m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;option         redispatch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;balance        roundrobin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;default-server inter 2s rise 3 fall 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;option         dontlognull&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;option         dontlog-normal&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;extra_sections&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;listen stats :1937&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;stats enable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;stats uri /&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;stats realm Haproxy Statistics&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;That&amp;#39;s all there is to it. Special thanks to Airbnb for open-sourcing their tool,
which allowed us to set up service discovery in a simple and scalable way. For those
not on Amazon EC2, there is a &lt;a href=&quot;http://zookeeper.apache.org/&quot;&gt;Zookeeper&lt;/a&gt; watcher (which we did not want to deal with),
and hopefully soon an etcd watcher:&lt;br&gt;
&lt;a href=&quot;https://github.com/airbnb/synapse/pull/58&quot;&gt;https://github.com/airbnb/synapse/pull/58&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that&amp;#39;s merged, we may move to using &lt;a href=&quot;https://github.com/airbnb/nerve&quot;&gt;Nerve&lt;/a&gt; with &lt;a href=&quot;https://github.com/coreos/etcd&quot;&gt;etcd&lt;/a&gt; instead of EC2 tags to handle service announcement. For reference, I&amp;#39;ll leave this etcd example docker information here:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl https://discovery.etcd.io/new?size=3
docker run
    --restart always
    --name etcd
    -d
    -p 2379:2379
    -p 2380:2380
    -v /opt/etcd:/opt/etcd
    -v /var/log:/var/log
    -v /etc/ssl/certs:/etc/ssl/certs
    quay.io/coreos/etcd:v2.0.0
    -data-dir /opt/etcd
    -name etcd-unique-name
    -listen-client-urls http://0.0.0.0:2379
    -listen-peer-urls http://0.0.0.0:2380
    -advertise-client-urls http://localhost:2379
    -initial-advertise-peer-urls http://localhost:2380
    -discovery https://discovery.etcd.io/XXXXXXXXXXXX
    -initial-cluster-token cluster-token-here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(see &lt;a href=&quot;https://coreos.com/docs/cluster-management/setup/cluster-discovery/&quot;&gt;discovery docs&lt;/a&gt; for url)&lt;/p&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>10x: Docker at Clay.io</title><link href="https://zolmeister.com/2014/12/10x-docker-at-clay-io.html" rel="alternate" type="text/html" title="10x: Docker at Clay.io" /><published>2014-12-15T00:00:00-06:00</published><updated>2014-12-15T00:00:00-06:00</updated><id>https://zolmeister.com/2014/12/10x-docker-at-clay-io</id><content type="html" xml:base="https://zolmeister.com/2014/12/10x-docker-at-clay-io.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/docker_logo.png&quot; alt=&quot;docker logo&quot;&gt;&lt;/p&gt;

&lt;h2&gt;Intro&lt;/h2&gt;

&lt;p&gt;At &lt;a href=&quot;http://clay.io/&quot;&gt;Clay&lt;/a&gt;, our deploy process is fun. Seriously. Because we use Docker.&lt;/p&gt;

&lt;p&gt;Docker is a containerization tool that allows for easy application isolation and deployment.
The basic idea is that Docker runs your application in a virtualized isolated environment,
similar to a virtual machine, but without the overhead. You start with a &amp;#39;base image&amp;#39;,
and then describe how to create a &amp;#39;container&amp;#39; using a Dockerfile.&lt;/p&gt;

&lt;h2&gt;Overview&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/docker-diagram.png&quot; alt=&quot;docker diagram&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href=&quot;https://gist.github.com/Zolmeister/aa0cf06678170a39c3c0&quot;&gt;Example Ansible Config&lt;/a&gt; (may not follow best practices)&lt;/p&gt;

&lt;p&gt;Our deploy process looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merge all code to be shipped into git &lt;code&gt;master&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git tag&lt;/code&gt; for release (eg. v1.0.0)&lt;/li&gt;
&lt;li&gt;Build docker image (code lock)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker tag&lt;/code&gt; for release (eg. v1.0.0)&lt;/li&gt;
&lt;li&gt;Push docker image to the registry&lt;/li&gt;
&lt;li&gt;Update staging cluster container versions (zero downtime)

&lt;ul&gt;
&lt;li&gt;Make sure backup container is running&lt;/li&gt;
&lt;li&gt;Upgrade master container&lt;/li&gt;
&lt;li&gt;Wait for new master to be up&lt;/li&gt;
&lt;li&gt;Upgrade backup container&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Update production cluster container versions (zero downtime)

&lt;ul&gt;
&lt;li&gt;(see above process)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process guarantees that we are running the exact same code in staging and production,
and allows us to roll back to the last docker container release version if anything goes wrong.&lt;/p&gt;

&lt;h2&gt;Clay.io Dockerfiles&lt;/h2&gt;

&lt;p&gt;Here is an example Dockerfile from our mobile app (&lt;a href=&quot;https://github.com/claydotio/clay-mobile/blob/master/Dockerfile&quot;&gt;source&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-docker&quot; data-lang=&quot;docker&quot;&gt;FROM dockerfile/nodejs:latest

# Install Git
RUN apt-get install -y git

# Add source
ADD ./node_modules /opt/clay-mobile/node_modules
ADD . /opt/clay-mobile

WORKDIR /opt/clay-mobile

# Install app deps
RUN npm install

CMD [&quot;npm&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The file is self-explanatory. It simply copies the source code from the current directory into the container.
Environment variables will be used to introduce sensitive / dynamic configuration.
One important note is that &lt;code&gt;npm start&lt;/code&gt; actually compiles and minifies the code for production:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// package.json&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;node_modules/gulp/bin/gulp.js build&quot;&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;npm run build &amp;amp;&amp;amp;
              ./node_modules/pm2/bin/pm2 start ./bin/server.coffee
                -i max
                --name clay_mobile
                --no-daemon
                -o /var/log/clay/clay_mobile.log
                -e /var/log/clay/clay_mobile.error.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is because we need to re-build the static files with the production environment variables.
For more examples, checkout our GitHub: &lt;a href=&quot;https://github.com/claydotio&quot;&gt;github.com/clay.io&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Basic Deployment of A Docker Image&lt;/h2&gt;

&lt;p&gt;At Clay, we host our images on the &lt;a href=&quot;https://registry.hub.docker.com/repos/clay/&quot;&gt;docker registry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because of Docker, deploying our applications to staging and production environments is trivial.
This is the entire process (untagged container, to staging - automated with &lt;a href=&quot;http://www.ansible.com/home&quot;&gt;Ansible&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Local machine / Build server&lt;/span&gt;
docker build -t clay/mobile .
docker push clay/mobile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Staging / Production server&lt;/span&gt;
docker pull clay/mobile
docker run
    --restart on-failure
    -v /var/log/clay:/var/log/clay
    -p 50000:3000
    -e &lt;span class=&quot;nv&quot;&gt;CLAY_MOBILE_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;XXXX
    -e &lt;span class=&quot;nv&quot;&gt;CLAY_API_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;XXXX
    -e &lt;span class=&quot;nv&quot;&gt;FC_API_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;XXXX
    -e &lt;span class=&quot;nv&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production
    -e &lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3000
    --name mobile
    -d
    -t clay/mobile:VERSION
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(Note: Ports 49,152 - 65,535 are generally used for private applications)&lt;/p&gt;

&lt;h2&gt;Zero-Downtime Updates&lt;/h2&gt;

&lt;p&gt;If you noticed in the above start script, we use &lt;a href=&quot;https://github.com/Unitech/pm2&quot;&gt;PM2&lt;/a&gt; to handle clustering multiple server processes.
PM2 supports zero-downtime code updates, however because it is inside the container and we never change code within a container at runtime, we don&amp;#39;t use it.
PM2 is used simply to take advantage of multiple server cores.&lt;/p&gt;

&lt;p&gt;The key to zero-downtime updates is to run two server processes. A master process, and a backup process.
We do this by assigning different ports to two container deployments:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run ... -e &lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;50000 --name mobile
docker run ... -e &lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;50001 --name mobile-backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://www.haproxy.org/&quot;&gt;HAProxy&lt;/a&gt; handles re-routing traffic to the backup server when the master goes down:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;# example haproxy.cfg

backend mobile
  mode http
  balance roundrobin
  server app1 x.x.x.x:50000 check
  server app1b x.x.x.x:50001 check backup
  server app2 x.x.x.x:50000 check
  server app2b x.x.x.x:50001 check backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A future post on HAProxy will go into more details on how we use HAProxy to load balance across our servers.&lt;/p&gt;

&lt;p&gt;The following deploy process is fully automated by Ansible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify backup healthy container status (Ansible)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker pull clay/mobile:v1.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Kill master container (network requests re-route automatically to the backup)

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker rm -f mobile&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Update master container, and restart

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run ...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Once the master container comes back up, network requests will move back to the master&lt;/li&gt;
&lt;li&gt;Kill and update the backup container

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker rm -f mobile-backup &amp;amp;&amp;amp; docker run ...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If anything goes wrong, simply revert to an older image version:&lt;br&gt;
&lt;code&gt;docker run -t clay/mobile:v0.0.12&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Closing&lt;/h2&gt;

&lt;p&gt;If you missed previous &lt;code&gt;10x&lt;/code&gt; posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://zolmeister.com/2014/10/10x-architecture-at-clay-io.html&quot;&gt;Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zolmeister.com/2014/10/10x-logging-at-clay-io.html&quot;&gt;Logging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>10x: Logging at Clay.io</title><link href="https://zolmeister.com/2014/10/10x-logging-at-clay-io.html" rel="alternate" type="text/html" title="10x: Logging at Clay.io" /><published>2014-10-25T00:00:00-05:00</published><updated>2014-10-25T00:00:00-05:00</updated><id>https://zolmeister.com/2014/10/10x-logging-at-clay-io</id><content type="html" xml:base="https://zolmeister.com/2014/10/10x-logging-at-clay-io.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/logstash_logo.png&quot; alt=&quot;logstash logo&quot;&gt;&lt;/p&gt;

&lt;p&gt;Managing 20+ servers as a small team is no easy task, and when things go wrong
(they always do) figuring out what happened quickly is essential. Of course
we can&amp;#39;t ssh into each machine, that would take ages, so instead we use &lt;a href=&quot;http://logstash.net/&quot;&gt;Logstash&lt;/a&gt;
to aggregate our logs.&lt;/p&gt;

&lt;p&gt;This is the second post in my &lt;code&gt;10x&lt;/code&gt; series, and if you missed last episode: &lt;a href=&quot;http://zolmeister.com/2014/10/10x-architecture-at-clay-io.html&quot;&gt;Architecture at Clay.io&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Logstash overview&lt;/h4&gt;

&lt;p&gt;Logstash deployments have two parts. The aggregate server (or cluster), and the
client servers. The aggregate server runs the main &lt;code&gt;logstash&lt;/code&gt; binary, while the
client servers run &lt;code&gt;logstash-forwarder&lt;/code&gt; to ships logs to the &lt;code&gt;logstash&lt;/code&gt; instance.
Here is a diagram of our setup:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/scribe.png&quot; alt=&quot;scribe&quot;&gt;&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;logstash-forwarder&lt;/code&gt; doesn&amp;#39;t rotate logs. Log rotation is the process of
storing older logs (usually compressed) into buckets (either of size or by date)
and then eventually expiring them. The (aptly named) tool for the job here is
the &lt;a href=&quot;http://linuxcommand.org/man_pages/logrotate8.html&quot;&gt;&lt;code&gt;logrotate&lt;/code&gt;&lt;/a&gt; linux utility.&lt;/p&gt;

&lt;h4&gt;Investigating logs&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/logstash_screenshot.png&quot; alt=&quot;kibana&quot;&gt;&lt;/p&gt;

&lt;p&gt;The logstash daemon runs &lt;a href=&quot;http://www.elasticsearch.org/&quot;&gt;ElasticSearch&lt;/a&gt; and
&lt;a href=&quot;http://www.elasticsearch.org/overview/kibana/&quot;&gt;Kibana&lt;/a&gt; to analyze logs.
You can apply many filters and create complex queries into your log data
this way because of ElasticSearch.&lt;/p&gt;

&lt;p&gt;Note that you cannot expose ElasticSearch ports publicly or your server will
get hacked (!). This happened to us, and our solution was to move our infrastructure
to &lt;a href=&quot;http://aws.amazon.com/vpc/&quot;&gt;Amazon VPC&lt;/a&gt; so that the only way to get to the logs
is to be inside of our privileged network.&lt;/p&gt;

&lt;h4&gt;Journalist + Scribe&lt;/h4&gt;

&lt;p&gt;At Clay.io we have open-sourced our Docker containers so that anyone can deploy
a distributed logging system themselves in just a few seconds.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Logstash&lt;/code&gt; Server: &lt;a href=&quot;https://github.com/claydotio/journalist-public&quot;&gt;clay/journalist-public&lt;/a&gt;&lt;br&gt;
&lt;code&gt;Logstash-forwarder&lt;/code&gt; Server: &lt;a href=&quot;https://github.com/claydotio/scribe-public&quot;&gt;clay/scribe-public&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good Luck!&lt;/p&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>10x: Architecture at Clay.io</title><link href="https://zolmeister.com/2014/10/10x-architecture-at-clay-io.html" rel="alternate" type="text/html" title="10x: Architecture at Clay.io" /><published>2014-10-01T00:00:00-05:00</published><updated>2014-10-01T00:00:00-05:00</updated><id>https://zolmeister.com/2014/10/10x-architecture-at-clay-io</id><content type="html" xml:base="https://zolmeister.com/2014/10/10x-architecture-at-clay-io.html">&lt;p&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/clay-architecture-1.png&quot; alt=&quot;Clay.io&quot;&gt;&lt;/p&gt;

&lt;p&gt;This is the first post in my new series &lt;code&gt;10x&lt;/code&gt;, where I share my experiences
and how we do things at &lt;a href=&quot;http://clay.io/&quot;&gt;Clay.io&lt;/a&gt; to develop at scale with a small team.&lt;/p&gt;

&lt;p&gt;Update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://zolmeister.com/2014/10/10x-logging-at-clay-io.html&quot;&gt;10x: Logging at Clay.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zolmeister.com/2014/12/10x-docker-at-clay-io.html&quot;&gt;10x: Docker at Clay.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Cloud&lt;/h2&gt;

&lt;h4&gt;CloudFlare&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/cloudflare-logo.png&quot; alt=&quot;CloudFlare&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;CloudFlare&lt;/a&gt; handles all of our DNS, and acts as a distributed caching
proxy with some additional DDOS protection features. It also handles SSL.&lt;/p&gt;

&lt;h4&gt;Amazon EC2 + VPC + NAT server&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://aws.amazon.com/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/aws-logo.png&quot; alt=&quot;Amazon Web Services&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Almost all of our servers live on Amazon EC2, most are either medium or large instances.
We also use Amazon VPC to host some of our servers inside of a private network,
inaccessible from the outside world. In order to get into this private network we have
a NAT server, which also serves as our VPN endpoint which we use when working
with our internal network. (&lt;a href=&quot;http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html&quot;&gt;guide&lt;/a&gt;, &lt;a href=&quot;https://openvpn.net/index.php/open-source/documentation/howto.html&quot;&gt;OpenVPN&lt;/a&gt;)&lt;/p&gt;

&lt;h4&gt;Amazon S3&lt;/h4&gt;

&lt;p&gt;We use Amazon S3 as our CDN backend, which hosts all of our static content.
We use a separate domain for this: &lt;code&gt;cdn.wtf&lt;/code&gt; for security and performance
reasons (cookie-less domain).&lt;/p&gt;

&lt;h4&gt;HAProxy&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.haproxy.org/&quot;&gt;HAProxy&lt;/a&gt; is an extremely performant reverse-proxy which we use to route traffic to
our different services. This work is non-trivial due to the nature of Clay.io and its
platform support concerns (and legacy code support), which I will go into detail in a later article.&lt;/p&gt;

&lt;p&gt;We currently have a single HAProxy server on an m3.medium instance, but will upgrade as traffic increases.
In addition, we may add Amazon ELB in front to scale horizontally if necessary.&lt;/p&gt;

&lt;h4&gt;App Server - Docker&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://www.docker.com/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/docker-logo.png&quot; alt=&quot;Docker&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; is tool to manage Linux containers, which are similar to Virtual Machines
except with less overhead (and without some isolation and security guarantees).
The key to Docker is that code shipped inside of a container should run the same
no matter what the host machine looks like.&lt;/p&gt;

&lt;p&gt;We currently run most of our computational services on an &lt;code&gt;app server&lt;/code&gt; via Docker.
This server can easily be replicated to meet elastic demand, and services can be moved on and off easily.
Eventually we hope to manage these app servers with a tool like Kubernetes. (See bottom of post)&lt;/p&gt;

&lt;h4&gt;Staging App Server - Docker&lt;/h4&gt;

&lt;p&gt;Our staging environment server is identical to our application server, and
runs the exact same docker binaries that we run in production. This environment
has been critical to preventing unnecessary breakage and downtime of our production systems.&lt;/p&gt;

&lt;h2&gt;Data&lt;/h2&gt;

&lt;h4&gt;MySQL&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mysql.com/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/mysql-logo.jpg&quot; alt=&quot;MySQL&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mysql.com/&quot;&gt;MySQL&lt;/a&gt; is a production-hardened relational SQL database.
The vast majority of our data currently resides inside a Master-Slave MySQL cluster.
We have one master, and two slave servers which serve most of our queries for our users.
Eventually we may have to move tables or shard the single master server, but hopefully not for a while.&lt;/p&gt;

&lt;h4&gt;Logstash&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://logstash.net/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/logstash-logo.png&quot; alt=&quot;logstash&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://logstash.net/&quot;&gt;Logstash&lt;/a&gt; is a log aggregation tool, with Kibana integration for analysis.
It basically handles all of our application logs, and gives us a place to check
for errors when something goes wrong. It saves us from having to SSH into a machine to check logs.&lt;/p&gt;

&lt;h4&gt;MongoDB&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mongodb.org/&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/mongoDB-logo.png&quot; alt=&quot;MongoDB&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt; is a NoSQL document storage database.
We currently use mongodb for some of our developer endpoints, and for our A/B testing
framework &lt;a href=&quot;https://github.com/claydotio/flak-cannon&quot;&gt;Flak Cannon&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Memcached&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://memcached.org/&quot;&gt;Memcached&lt;/a&gt; is a key-value store, used mostly for caching. In many ways it is similar to Redis.
We currently use Memcached in our legacy webapp for caching MySQL query results.
Eventually we would like to replace this with Redis.&lt;/p&gt;

&lt;h2&gt;DevOps&lt;/h2&gt;

&lt;h4&gt;Ansible&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.ansible.com/home&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/ansible-logo.png&quot; alt=&quot;Ansible&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.ansible.com/home&quot;&gt;Ansible&lt;/a&gt; has been our tool of choice for managing our servers. It&amp;#39;s simple enough for most developers to learn quickly
and be comfortable working with, and has been critical for automating many of the processes normally
managed by an operations team.&lt;/p&gt;

&lt;h2&gt;Other Services&lt;/h2&gt;

&lt;h4&gt;GitHub&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; - Great source code management, enough said.&lt;/p&gt;

&lt;h4&gt;Uptime Robot&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://uptimerobot.com/&quot;&gt;Uptime Robot&lt;/a&gt; is a &lt;strong&gt;free&lt;/strong&gt; monitoring service which we use to monitor our healthchecks and endpoints.
If anything goes down, it will email and text message us within 5min.&lt;/p&gt;

&lt;h4&gt;Drone.io&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://drone.io/&quot;&gt;Drone.io&lt;/a&gt; is a continuous integration service, which we use to continuously run our
test suites for our various projects. It is similar to TravisCI, and has recently
released an open source self-hostable version.&lt;/p&gt;

&lt;h4&gt;Docker Registry&lt;/h4&gt;

&lt;p&gt;We currently use the official &lt;a href=&quot;https://registry.hub.docker.com/&quot;&gt;Docker registry&lt;/a&gt; to manage our docker containers.
It&amp;#39;s similar to GitHub, except for Docker containers.&lt;/p&gt;

&lt;h4&gt;New Relic&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://newrelic.com/&quot;&gt;New Relic&lt;/a&gt; is a server and application monitoring service, which we mostly use to
monitor our servers to let us know when a machine is running out of disk or memory&lt;/p&gt;

&lt;h4&gt;Google Analytics&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google Analytics&lt;/a&gt; is our main website analytics tracking tool.
For tracking our site specific features, we use the custom events features.&lt;/p&gt;

&lt;h4&gt;Google Apps&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.google.com/enterprise/apps/business/&quot;&gt;Google Apps&lt;/a&gt; provides email for our domain clay.io, and gives our organization a shared
Google Drive setup.&lt;/p&gt;

&lt;h4&gt;Last Pass&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://lastpass.com/&quot;&gt;Last Pass&lt;/a&gt; is a password management service which allows us to share company
credentials for all of the other above services easily amongst the team.&lt;/p&gt;

&lt;h2&gt;The Future&lt;/h2&gt;

&lt;p&gt;While we are currently happy with our setup today, we hope to improve it in the coming months.
This initial infrastructure version is missing some features which weren&amp;#39;t necessary enough
to justify spending time on, but we will eventually need to come back to them as we scale.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleCloudPlatform/kubernetes&quot;&gt;Kubernetes&lt;/a&gt; is looking to be an amazing project and tool for managing Docker containers at scale.
We will be following it&amp;#39;s development closely and hopefully put it into production as the project matures.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://aws.amazon.com/glacier/&quot;&gt;Amazon Glacier&lt;/a&gt; is another technology we have been looking at for doing database backups,
and hope to implement that in the near future.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://rethinkdb.com/&quot;&gt;RethinkDB&lt;/a&gt;, while quite immature, is also quite an interesting project. We will definitely be following it&amp;#39;s
progress and may eventually move some of our data into it as we move away from MySQL.&lt;/p&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>Developing on Linux</title><link href="https://zolmeister.com/2014/09/developing-on-linux.html" rel="alternate" type="text/html" title="Developing on Linux" /><published>2014-09-13T00:00:00-05:00</published><updated>2014-09-13T00:00:00-05:00</updated><id>https://zolmeister.com/2014/09/developing-on-linux</id><content type="html" xml:base="https://zolmeister.com/2014/09/developing-on-linux.html">&lt;p&gt;&lt;a href=&quot;https://github.com/Zolmeister/dotfiles&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/desktop.png&quot; alt=&quot;desktop&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;&lt;a href=&quot;https://github.com/Zolmeister/dotfiles&quot;&gt;dotfiles&lt;/a&gt;&lt;/h3&gt;

&lt;h2&gt;i3 - Tiling Window Manager&lt;/h2&gt;

&lt;p&gt;A tiling window manager is a type of window manager that arranges your windows as tiles,
usually in such a way that makes managing them more efficient via keyboard shortcuts.
&lt;a href=&quot;http://i3wm.org/&quot;&gt;i3&lt;/a&gt; is my window manager of choice, after having used &lt;a href=&quot;http://xmonad.org/&quot;&gt;xmonad&lt;/a&gt;
for a long time, i3 provides more flexibility and ease of configuration.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href=&quot;https://www.youtube.com/watch?v=Wx0eNaGzAZU&quot;&gt;great sceencast&lt;/a&gt; which will help you understand the power of tiling window managers.
i3 does a great job of managing multiple workspaces, even across multiple monitors.
While developing I actively use 7 distinct workspaces across two monitors (soon probably three)
and having an efficient way of transitioning (&lt;code&gt;alt-#&lt;/code&gt;) is critical to maintaining my workflow speed.&lt;/p&gt;

&lt;p&gt;Here is a bit of config that I added to i3 (see dotfiles for the full version)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;# Zolmeister
# disabled status bar (see above)
# screen lock with ctrl + sup + l
bindsym Control+mod4+l exec i3lock -c 000000 -i ~/Pictures/Z/Z-bg.png
# set border to 1
new_window 1pixel
# hide border if 1 window
hide_edge_borders both
# Resize Containers, Vim-style                            ($mod+Control+[hjkl])
bindsym $mod+Control+j resize grow height 5 px or 5 ppt
bindsym $mod+Control+k resize shrink height 5 px or 5 ppt
bindsym $mod+Control+l resize grow width 5 px or 5 ppt
bindsym $mod+Control+h resize shrink width 5 px or 5 ppt

# custom colors
# class                 border  backgr. text    indicator
client.focused                #1793D0    #1793D0    #FFFFFF
client.focused_inactive    #333333    #333333    #999999
client.unfocused            #333333    #333333    #999999
client.urgent                #FF0000    #8C5665    #FF0000

# background image
exec --no-startup-id nitrogen --restore

# fix media keys and gtk theme issues
exec gnome-settings-daemon &amp;amp;

# fix deb file installation
exec /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;ZSH + Powerline Shell&lt;/h2&gt;

&lt;p&gt;Most shells today run [Bash](http://en.wikipedia.org/wiki/Bash&lt;em&gt;(Unix&lt;/em&gt;shell)
however &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;ZSH&lt;/a&gt; is an alternative which
has great autocompletion and &lt;a href=&quot;http://www.slideshare.net/jaguardesignstudio/why-zsh-is-cooler-than-your-shell-16194692&quot;&gt;other nifty feature&lt;/a&gt;
For my &lt;code&gt;.zshrc&lt;/code&gt; file, check my dotfiles.&lt;/p&gt;

&lt;p&gt;In addition, the ZSH framework &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;oh-my-zsh&lt;/a&gt;
provides some amazing features and
&lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh/wiki/Cheatsheet&quot;&gt;shortcuts&lt;/a&gt;,
definitely work checking out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/milkbikis/powerline-shell&quot;&gt;Powerline shell&lt;/a&gt; is a beautiful
shell extension which takes care of drawing your prompt. I have customized it
based on my ZSH theme to include additional git hints (like ±).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/shell.png&quot; alt=&quot;shell&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Atom.io&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://atom.io/&quot;&gt;Atom.io&lt;/a&gt; is without a doubt the best editor I have ever used.
I have used &lt;a href=&quot;http://www.aptana.com/&quot;&gt;Aptana&lt;/a&gt; (good, but bloated),
&lt;a href=&quot;http://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt; (excellent, but not open source),
and &lt;a href=&quot;http://brackets.io/?lang=en&quot;&gt;Brackets&lt;/a&gt; (ok, missing quite a few features) extensively.
None of them compare to Atom (I refuse to use an editor that is not open-source, as
my editor is a core part of my development workflow and I must have full control over it).&lt;/p&gt;

&lt;p&gt;Some day I may switch to VIM/Emacs, but have been quite happy with my working speed in Atom.&lt;/p&gt;

&lt;h2&gt;Hardware&lt;/h2&gt;

&lt;p&gt;After &lt;a href=&quot;http://zolmeister.com/2014/07/cto-cofounder-clay-io.html&quot;&gt;joining Clay.io&lt;/a&gt;
I have finally gotten around to building another desktop. The only spec requirements I had
were a Linux friendly GPU (Nvidia), 32GB of RAM, and 4k monitor support.&lt;/p&gt;

&lt;p&gt;Here is the build (&lt;a href=&quot;http://secure.newegg.com/WishList/PublicWishDetail.aspx?WishListNumber=25084652&quot;&gt;newegg wishlist&lt;/a&gt;, missing PSU + case):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=0JC-0007-00009&quot;&gt;28&amp;quot; 4K monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16820148800&quot;&gt;32GB DDR3 RAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16819117369&quot;&gt;i7 4790K&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16820148820&quot;&gt;256GB SSD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16814127748&quot;&gt;GeForce GTX 760&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B003J89V0A/ref=oh_aui_detailpage_o03_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;1000W PSU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16822148834&quot;&gt;2TB HD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16824009316&quot;&gt;21.5&amp;quot; monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16811146061&quot;&gt;NZXT ATX Mid Tower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.newegg.com/Product/Product.aspx?Item=N82E16813157372&quot;&gt;ASRock Motherboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry><entry><title>Fuco - A Coloring Game</title><link href="https://zolmeister.com/2014/09/Fuco-a-coloring-game.html" rel="alternate" type="text/html" title="Fuco - A Coloring Game" /><published>2014-09-01T00:00:00-05:00</published><updated>2014-09-01T00:00:00-05:00</updated><id>https://zolmeister.com/2014/09/Fuco-a-coloring-game</id><content type="html" xml:base="https://zolmeister.com/2014/09/Fuco-a-coloring-game.html">&lt;p&gt;&lt;a href=&quot;http://fuco.zolmeister.com&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/fuco_logo.png&quot; alt=&quot;fuco&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://fuco.zolmeister.com/&quot;&gt;Web&lt;/a&gt; |
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.zolmeister.fuco&quot;&gt;Android&lt;/a&gt; |
&lt;a href=&quot;https://chrome.google.com/webstore/detail/fuco/nknbogijbbpbhkfpojekilofmfbbmccj&quot;&gt;Chrome&lt;/a&gt; |
&lt;a href=&quot;http://www.amazon.com/gp/product/B00N0O21FU&quot;&gt;Amazon&lt;/a&gt; |
&lt;a href=&quot;https://marketplace.firefox.com/app/fuco/&quot;&gt;Firefox&lt;/a&gt; |
&lt;a href=&quot;https://github.com/Zolmeister/fuco&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Welcome, to Fuco (&lt;a href=&quot;https://github.com/Zolmeister/fuco&quot;&gt;Source&lt;/a&gt;),
a coloring game! Fuco was made in 48 hours as part of the
&lt;a href=&quot;http://www.ludumdare.com/compo/ludum-dare-30/?action=preview&amp;amp;uid=29241&quot;&gt;Ludum Dare game compo&lt;/a&gt;.
The theme was &lt;code&gt;Connected Worlds&lt;/code&gt;, which game me the inspiration for Fuco.
Originally I had an idea for a game that mixed game art styles (pixel art + isometric + full 3D),
however that was far too ambitious considering the time constraints and my artistic skills.&lt;/p&gt;

&lt;p&gt;This was my second Ludum Dare, my first game being &lt;a href=&quot;http://zolmeister.com/2013/12/nent.html&quot;&gt;Nent&lt;/a&gt;
which took advantage of WebGL shaders for a metaballs effect. Since I &lt;a href=&quot;http://zolmeister.com/2014/07/cto-cofounder-clay-io.html&quot;&gt;joined Clay.io&lt;/a&gt;
I figured that this time I would make a game that could be played on mobile,
which meant no WebGL (due to iOS support). Because I wrote platform-agnostic code, I was easily able to publish the game to many platforms &lt;a href=&quot;http://zolmeister.com/2014/01/how-to-turn-webapp-into-native-android.html&quot;&gt;using Cordova&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being my second time participating I should have had better time management.
However I made most of the game on Sunday (pulling an all-nighter, being awake during the &lt;a href=&quot;http://america.aljazeera.com/articles/2014/8/24/san-francisco-earthquake.html&quot;&gt;earthquake&lt;/a&gt; at 3am):
&lt;a href=&quot;https://github.com/Zolmeister/fuco&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/fuco_github_timeline.png&quot; alt=&quot;timeline&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;iframe frameborder=0 src=&quot;http://fuco.zolmeister.com&quot; style=&quot;width: 300px; height: 500px; margin: 0 auto&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h4&gt;Negative coloring without shaders&lt;/h4&gt;

&lt;p&gt;When you play the game, you&amp;#39;ll notice that coloring the correct area below will erase the
color above, and an incorrect coloration will override the above color (incorrectly).
Solving this problem in a performant way on HTML5 Canvas was not immediately obvious.
If I had been able to use WebGL, I could easily write a shader which could do calculations
based on each pixel value. However canvas is too slow to update each pixel each frame
which meant my solution had to be based on native canvas calls.&lt;/p&gt;

&lt;p&gt;Each section of the circle is defined by a closed shape, so I simply erase
the region the player is brushing using &lt;code&gt;ctx.clip()&lt;/code&gt; which allows me to mask draw calls.
Here is my solution:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// erase inside selected brush clip region&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;beginPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;radgrad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRadialGradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;radgrad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addColorStop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;radgrad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addColorStop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;radgrad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addColorStop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fillStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;radgrad&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fillRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fillRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brushSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://fuco.zolmeister.com&quot;&gt;&lt;img src=&quot;https://zolmeister.com/assets/images/fuco_screenshot.png&quot; alt=&quot;fuco&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Home screen animation&lt;/h4&gt;

&lt;p&gt;One of my favorite effects I made for this game was the home screen animation of the logo.
Some googling for SVG animation landed me at &lt;a href=&quot;http://codepen.io/netsi1964/pen/iyglK&quot;&gt;this example&lt;/a&gt;,
which I adapted slightly for my purposes. I used Inkscape to draw the SVG paths,
and them inlined the SVG into the HTML.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$paths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'path'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This is hard coded because it crashes android if you calculate it&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pathLengths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;114.63752746582031&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;135.55255126953125&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;115.6463851928711&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;147.36000061035156&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3442.748291015625&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1806.394775390625&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8450.58984375&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8450.58984375&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'load'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This line will crash android browser clients&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// pathLengths[i] = $paths[i].getTotalLength()&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;simulatePathDrawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;visibility&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'visible'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;simulatePathDrawing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pathLengths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;transition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;WebkitTransition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strokeDasharray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strokeDashoffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;transition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;WebkitTransition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'stroke-dashoffset 1.25s ease-in-out'&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strokeDashoffset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0'&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strokeWidth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2px'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><author><name>Zoli Kahan</name></author><summary></summary></entry></feed>
