<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[BL Blog - Medium]]></title>
        <description><![CDATA[Words about things - Medium]]></description>
        <link>https://medium.com/bl-blog?source=rss----b4434f474a15---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>BL Blog - Medium</title>
            <link>https://medium.com/bl-blog?source=rss----b4434f474a15---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 21:53:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/bl-blog" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Architecture: Consider Kron]]></title>
            <link>https://medium.com/bl-blog/architecture-consider-kron-1ee29412573e?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/1ee29412573e</guid>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[cron]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[ruby-on-rails]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Tue, 02 May 2017 05:08:38 GMT</pubDate>
            <atom:updated>2017-05-02T05:08:24.022Z</atom:updated>
            <content:encoded><![CDATA[<p>The last post in our architecture series discussed <a href="https://bleonard.com/architecture-background-processing-d4b6af9ad6b1">background processing</a>. There is a special type of background processing that I wanted to make a quick note about. These are things that need to be done periodically or otherwise on a schedule.</p><p>In our internal speak, we call this a “kron” job. If you are familiar with <a href="https://en.wikipedia.org/wiki/Cron">cron</a> jobs, it’s the same idea. A product manager misspelled it once and it stuck! We don’t actually use regular cron infrastructure, so the spelling <em>nuance</em> is helpful.</p><p>The specifics of how we implement it involve our <a href="https://bleonard.com/blog/2015/04/02/queue-bus/">message bus</a> infrastructure, but I think the concept and the decisions involved could include many other implementations.</p><h3>When to use it</h3><p>Let’s take the job from the <a href="https://bleonard.com/architecture-background-processing-d4b6af9ad6b1">previous article</a>. The “charge an invoice 24 hours later” case is an interesting one. The system certainly supports delaying that code to run for an arbitrary time, but that’s not always the best idea.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/570f4fbc2b45339f8ae340e74f1bbb95/href">https://medium.com/media/570f4fbc2b45339f8ae340e74f1bbb95/href</a></iframe><p>One reason would be memory. When there a lot of invoices (woot!), we still have to save the notion of what should be done somewhere until it gets processed. In this case, the <a href="https://redis.io/">Redis</a> instance will have it stored in memory. The memory could fill up and adding more workers won’t help because of the delay.</p><p>The second reason is stability. This is important stuff and Redis could have issues and lose the data. We made everything <a href="http://www.restapitutorial.com/lessons/idempotency.html">idempotent</a> and could recreate everything, but it would certainly be a huge hassle.</p><p>So when enqueueing something to run in the future, especially if it is important or a long time from now (more than a few minutes), we consider kron.</p><h3>Batch mode</h3><p>If we were going to accomplish the same things but on a schedule, the code would have to change in some way. I like the existing worker because it already has the good stuff from the last article: source of truth, knowing whether or not it still needs to be run, and mutual exclusion. When batch processing, I believe it’s also good to still operate on this one at a time where the count (memory for redis) is low or the risk of issues is high. Both are the case here.</p><p>To turn it into a batch processor we need to know what needs to be processed at any given moment. This is easy to determine because we have the needed? method. It looks to be invoices that are in the pending state. Sometimes we need to add a state column or other piece of data to know what needs to be in the batch but in this case we are good to go.</p><p>From there we can decide if we are going to update the class as-is or make a batch worker. A batch worker is its own worker and would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b3fae657f67ac3d7111b555ce81cba8c/href">https://medium.com/media/b3fae657f67ac3d7111b555ce81cba8c/href</a></iframe><p>That’s it. Because of the worker lock on InvoiceChargeWorker and the state checking, it would be ok even if we were to enqueue it twice or something. Making a custom batch worker also prevents us from running this code twice.</p><p>We could also stick it as a class method on the original:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/163d913eec08c2a06f9ba2c5bf410072/href">https://medium.com/media/163d913eec08c2a06f9ba2c5bf410072/href</a></iframe><h3>How it works</h3><p>Again, in any given architecture there is probably a best way to do it. For example, maybe <a href="https://medium.com/airbnb-engineering/chronos-a-replacement-for-cron-f05d7d986a9d">this</a> is a good way to do it on top of <a href="http://mesos.apache.org/">Mesos</a>.</p><p>The challenge is running something on a schedule. In this case, process all invoices that need to be paid. That is what regular cron is made to do. However, we do not want to run that on every box. If we did that, we would have serious race conditions and might pay an invoice twice. Rather, we want to run it once globally across the entire infrastructure or at least per service.</p><p>We could probably do this by noting in the devops setup that one of the servers is special. It should get the cron setup. We could use something like the <a href="https://github.com/javan/whenever">whenever gem</a> to say what to do and we would only run that on one box per system. It needs to be per system because it has to be able to know what worker to enqueue or, in general, what code to run.</p><p>What we do instead is have a single service that has a process that sends out a heartbeat on the <a href="https://bleonard.com/blog/2015/04/02/queue-bus/">message bus</a>. Every minute, it publishes an event that looks like this.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0f8662d5335abc24e501721092d5b034/href">https://medium.com/media/0f8662d5335abc24e501721092d5b034/href</a></iframe><p>The current code for the process is already checked into <a href="https://github.com/queue-bus/queue-bus">queue-bus</a> and ready to use <a href="https://github.com/queue-bus/queue-bus/blob/5c009a3b2d2d58994813bdca0dc78547a18b0295/lib/queue_bus/heartbeat.rb">here</a>.</p><p><a href="https://github.com/queue-bus/resque-bus">Resque bus</a> supports this using the <a href="https://github.com/resque/resque-scheduler">resque-scheduler gem</a>. It is setup off by calling QueueBus.heartbeat!. We make sure it’s setup every time we start up Resque.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e1af56dde4f830df3321fe9d01adb66a/href">https://medium.com/media/e1af56dde4f830df3321fe9d01adb66a/href</a></iframe><p>This setup is automatically called every time Resque starts.</p><h3>Usage</h3><p>So now we can subscribe to this event to run something every minute, hour, day, Monday, month, whatever.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/135bece03f29dc3caefff0929d11c870/href">https://medium.com/media/135bece03f29dc3caefff0929d11c870/href</a></iframe><h3>Summary</h3><p>So that is how “kron” works.</p><p>Over time, we have decided this is a much more reliable way to process items in the background when a delay is acceptable. By setting up some sort of centralized architecture for this, many services and subscribe in a way that is familiar and unsurprising. We have found a lot of value in that.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ee29412573e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/architecture-consider-kron-1ee29412573e">Architecture: Consider Kron</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Architecture: Background Processing]]></title>
            <link>https://medium.com/bl-blog/architecture-background-processing-d4b6af9ad6b1?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/d4b6af9ad6b1</guid>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[ruby-on-rails]]></category>
            <category><![CDATA[rescue]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Fri, 14 Apr 2017 22:09:53 GMT</pubDate>
            <atom:updated>2017-04-14T22:09:14.063Z</atom:updated>
            <content:encoded><![CDATA[<p>So we have a bunch of <a href="https://bleonard.com/architecture-models-a9fb75bf35fd">models</a> and are doing stuff with them in <a href="https://bleonard.com/architecture-service-objects-4f936fecf142">service objects</a>. The next thing we might need is to process some code in the background.</p><p>Not everything can be done inline from the API request. For example, we might need to geocode a user’s postal code when they change it in their account. Or when an invoice is created, we want to charge it 24 hours later.</p><p>When working with background jobs, we default to the following practices:</p><ul><li>Workers are enqueued with a dictionary of inputs</li><li>These inputs should be used to fetch data from the source of truth</li><li>Workers know how to check if they still need to run</li><li>Locking schemes should protect parallel execution</li></ul><h3>Enqueue</h3><p>When we enqueue a worker, we have found that it’s quite helpful to always use a dictionary (hash) of key/value pairs. <a href="https://github.com/resque/resque">Resque</a> and <a href="http://sidekiq.org/">Sidekiq</a> both take a list of arguments like <a href="https://github.com/mperham/sidekiq/wiki/Getting-Started">so</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e4af3c4edfc7104199929897ff407d30/href">https://medium.com/media/e4af3c4edfc7104199929897ff407d30/href</a></iframe><p>This has proved to be problematic when adding new parameters or having optional parameters. For example, if we add a new (third) input parameter, there might be stuff in the queue with the old two. When the new code gets deployed, it will throw an ‘invalid number of arguments’ type of error. When using a hash, we can give it a default, fail gracefully, or do whatever we like on a class by class basis.</p><p>So to provide better change management and optional arguments, we always do it like so:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/acb601f64fd098a778f644068e56a80b/href">https://medium.com/media/acb601f64fd098a778f644068e56a80b/href</a></iframe><h3>Source of Truth</h3><p>Let’s say we want to update a search index every time a user record is changed. We need to write their first name, last name, etc to <a href="https://www.elastic.co/">Elasticsearch</a>.</p><p>We could do something like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b264e5fc28df7e2e85a8c5fb8be4fbe3/href">https://medium.com/media/b264e5fc28df7e2e85a8c5fb8be4fbe3/href</a></iframe><p>This certainly would work, but is not considered best practice. It is better to be <a href="http://www.restapitutorial.com/lessons/idempotency.html">idempotent</a>. It writes everything that should ) by passing the minimal information to the background worker, who then looks up the source of truth. That way, if there is any delay between when it is enqueued and run, it will still send the correct information.</p><p>The better approach would look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d8640fc3414898ebc08ecfc932db799e/href">https://medium.com/media/d8640fc3414898ebc08ecfc932db799e/href</a></iframe><p>In the same vein, the worker should be in charge of whether or not it needs to do anything in the first place. For example, we can enqueue a worker to run later about an Invoice. If, at that time, the payment is Invoice still should be charged, then charge it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e9ccb8d24f908337d3f12c12b1726390/href">https://medium.com/media/e9ccb8d24f908337d3f12c12b1726390/href</a></iframe><p>This is another example of single source of truth. Even for jobs that are run immediately, this check is something we always put in place: return immediately if the worker is no longer relevant.</p><h3>Mutual Exclusion</h3><p>Let’s say the User object can sometimes change a few times rapidly. The “source of truth” approach will make sure the right thing always gets indexed. So that’s great. But it is pretty silly to index the same data twice or more times, right?</p><p>In this case, we add a queue lock. The effect is that if something is in the queue and waiting to be processed and you try to enqueue another one with the same inputs, then it will be a no-op. It looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/146085bce71b449eeb5ff68c1193d552/href">https://medium.com/media/146085bce71b449eeb5ff68c1193d552/href</a></iframe><p>Another case that often arises is mutual exclusion for <em>runtime</em>. Maybe weird payment things happen to the payment service if two invoices for the same user are happening at the same time.</p><p>In this case, we add a worker lock. The effect is that if something is in the queue and about to start running and there is another running at that moment, then it will re-enqueue itself to run later. It looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a829b42b29945ae0023c01e12f8e47b0/href">https://medium.com/media/a829b42b29945ae0023c01e12f8e47b0/href</a></iframe><p>For either type, you don’t have to lock on all the attributes or can (as shown in the last example) use calculations. The namespace of the lock is the worker class name. You can also set the namespace to allow locking between different workers.</p><h3>Message Bus</h3><p>Our <a href="https://www.bleonard.com/blog/2015/04/02/queue-bus/">message bus</a> and our use of background processes have a lot in common. In fact, the message bus is built on top of the same background processing infrastructure. The question that arises is this: when should something be enqueued directly and when should it publish and respond to a bus subscription?</p><p>The first note is that you should <em>always be publishing</em> (ABP). It doesn’t hurt anything to give (optional) visibility to other systems what is happening. Or use this as logging framework.</p><p>Just publishing, however, doesn’t mean we have to use that to do work in the background. Be can bother publish and enqueue a background worker. We enqueue a worker when the work in the background is essential to the correct operation of the use case at hand.</p><p>One example to enqueue directly would be the geocoding worker I mentioned earlier: when the user gives a new postal code, figure out where that is. It’s key to the account management system.</p><p>The search example I’ve been using might not actually be the best one because we would have the search system subscribed to changes in the account system. What I didn’t show that the enqueue call might actually happen from within a subscription.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8121b272a8511bf697c2d505fa1b06cc/href">https://medium.com/media/8121b272a8511bf697c2d505fa1b06cc/href</a></iframe><p>So these two concepts can work together. Why not just index it right in the subscription, though? A primary reason might be to use some of the locking mechanisms as the bus does not have that. It also might be the case that the worker is enqueued from other locations and this keeps things DRY. The worker is also easier to unit test.</p><h3>TResque</h3><p>We use <a href="https://github.com/resque/resque">Resque</a> as a base foundation and built on top of it with an abstraction layer called <a href="https://github.com/taskrabbit/tresque">TResque</a>. That’s TR (TaskRabbit) Resque. Get it? It puts all of these practices into place as well as adding and abstraction layer for the inevitable, but as yet unprioritized, move to <a href="http://sidekiq.org/">Sidekiq</a>.</p><p>I don’t necessarily expect anyone to use this, but it doesn’t hurt to make it available as an example of how we are using these tools.</p><p>You define a worker and enqueue things as show in the examples above. Then only layer left is around prioritization. You can give a queue name to a worker and then register what priority those workers are. If no queue is given, it is assumed to be the default queue.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c4624d28cb5b842811150b3cbcefa760/href">https://medium.com/media/c4624d28cb5b842811150b3cbcefa760/href</a></iframe><p>Then when you run Resque, you can use these registrations to process the queues in the right order.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/03232f015092163d3085c49195eae56f/href">https://medium.com/media/03232f015092163d3085c49195eae56f/href</a></iframe><p>This registration layer allows each of the systems (<a href="https://www.bleonard.com/blog/2014/02/11/rails-4-engines/">engines</a>) to work independently and still have centralized background processing.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4b6af9ad6b1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/architecture-background-processing-d4b6af9ad6b1">Architecture: Background Processing</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Architecture: Surface Area]]></title>
            <link>https://medium.com/bl-blog/architecture-surface-area-ba8271805033?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/ba8271805033</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[message-queue]]></category>
            <category><![CDATA[ruby-on-rails]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Tue, 11 Apr 2017 04:16:38 GMT</pubDate>
            <atom:updated>2017-04-11T04:17:47.290Z</atom:updated>
            <content:encoded><![CDATA[<p>The last post in the <a href="https://www.taskrabbit.com/">TaskRabbit</a> architecture series was about service <a href="https://bleonard.com/architecture-service-objects-4f936fecf142">objects</a>. This an example of what I call minimizing “surface area” of the code.</p><p>Frankly, I might be using the term wrong. It seems possible “surface area” usually refers to API signature of some objects. What I’m talking about here is the following train of thought:</p><ul><li>I change or add a line of code</li><li>What did I just affect?</li></ul><p>The “surface area” is the other things I have to look over. It is the area that I have to make sure has appropriate test coverage. Having a large surface area is what slows down development teams. The goal is to minimize it.</p><h3>Service Objects</h3><p>So how does our use of service objects relate to this concept?</p><p>Let’s say we have a new requirement that’s applicable when a Tasker submits an invoice that modifies what gets saved. If I were to add the code to the InvoiceJobOp from the <a href="https://bleonard.com/architecture-service-objects-4f936fecf142">previous article</a>, then it will only apply when the Op is run. If we were to do something in a before_save in the Invoice model, then it might accidentally kick in anytime an Invoice is changed.</p><p>That’s a lot more tests and things to keep in our mind. If it is just in the Op, that is less of those kinds of debt, so adding in the Op is an example of minimizing the surface area of the change.</p><h3>Namespacing</h3><p>We went through a <a href="https://bleonard.com/blog/2015/10/06/v2-retrospective/">roundabout journey</a> to end up where were we are. Many of the changes were about surface area and trying to reduce it.</p><p>People like microservices and SOA because of this same principle. We tried it and that part of it worked out really well. There was just no way that a change in service A could affect service B. As <a href="https://bleonard.com/blog/2015/10/06/v2-retrospective/">discussed</a>, however, we ran into issues in other dimensions.</p><p>Our current use of <a href="https://bleonard.com/blog/2014/02/11/rails-4-engines/">engines</a> follows the same approach to achieve the same surface area effect. It is all about namespacing. Modifying the user management engine can not affect the marketplace engine. This allows us to proceed with more confidence when making such changes.</p><p>A particular aspect of our setup is that any given model is “owned” by only one engine. The rest of the engines are allowed to read from the database but they cannot write. This provides sanity and minimizes the surface area. For example, the validations only need to live in one spot. You also know that no other code can go rogue and start messing with the data by accident or otherwise.</p><h3>Bus</h3><p>Of course, the world isn’t always cut and dry. Venn diagrams overlap. No abstraction or encapsulation is perfect. The seams in namespacing show up when something that happens in one service (engine) needs to affect something in another one.</p><p>For example, we were so happy just a few paragraphs ago that changes to the user management engine do not affect the marketplace engine. That is true and it is great. There is no direct effect from the code. However, as they tend to do, these pesky functional requirements always mess up perfect plans for the code. In this case, when a user changes their first name (in the account engine), the marketplace engine might need to update some data in <a href="https://www.elastic.co/">Elasticsearch</a>.</p><p>We use a <a href="https://bleonard.com/blog/2015/04/02/queue-bus/">message bus</a> to observe changes like this and react as appropriate.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ee7a5dc43e33e410aad4693925a366e0/href">https://medium.com/media/ee7a5dc43e33e410aad4693925a366e0/href</a></iframe><p>An important note here is that ProfileStoreWorker is <a href="http://www.restapitutorial.com/lessons/idempotency.html">idempotent</a>. It writes everything that should go in Elasticsearch every time. This technique reduces surface area by not depending on this single event and its contents, but rather only as a trigger.</p><p>One might say that these subscriptions are just as coupled as doing everything all in one spot. I see that point because, of course, the same things end up happening. However, we have this technique to be better for a few reasons.</p><ul><li>The trigger code (in the account engine) does not need to know about the rest of the system. It can mind its own business.</li><li>The subscribing code (in the marketplace engine) can be self-contained instead of being mixed up in the trigger code path.</li><li>Many different code paths might necessitate the ProfileStoreWorker to run. By decoupling it, we actually save complexity in many code paths.</li></ul><p>In code, developers tend to weave a tangled web wherein seemingly innocuous changes have far-reaching effects. We have been able to create more stable and agile code by considering the “surface area” of a change and minimizing it through some encapsulation and decoupling techniques.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ba8271805033" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/architecture-surface-area-ba8271805033">Architecture: Surface Area</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Architecture: Service Objects]]></title>
            <link>https://medium.com/bl-blog/architecture-service-objects-4f936fecf142?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/4f936fecf142</guid>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[ruby-on-rails]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Sun, 05 Mar 2017 22:12:23 GMT</pubDate>
            <atom:updated>2017-03-05T22:13:29.307Z</atom:updated>
            <content:encoded><![CDATA[<p>This is the second post in what is now indisputably a “series” of articles about how we build things at <a href="https://www.taskrabbit.com/">TaskRabbit</a>. Over time, we/I have internalized all kinds of lessons and patterns and are trying to take the time to write some of the key things down.</p><p>Building upwards from the <a href="https://medium.com/architecture-models-a9fb75bf35fd">last article about models</a>, let’s talk about how we use them. The models represent rows in the database in the <a href="http://guides.rubyonrails.org/active_record_basics.html">Rails ORM</a>. What code is deciding what to put in those rows and which ones should be created, etc? In our architecture, this role is filled by <a href="https://www.netguru.co/blog/service-objects-in-rails-will-help">service objects</a>.</p><p>Overall, we default to the following rules when using models in our system:</p><ul><li>Models contain data/state validations and methods tied directly to them</li><li>Models are manipulated by service objects that reflect the user experience</li></ul><h3>Something has to be fat</h3><p>In the beginning, there was <a href="http://ideum.com/2006/07/05/102/">Rails</a> and we saw that it was good. The world was optimized around the CRUD/REST use cases. Controllers had update_attributes and such. When there was more logic/nuance, it was put there in the controller (or the view).</p><p>There was a backlash of sorts against that and the new paradigm was <a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">“Fat model, skinny controller”</a>. The controllers were simple and emphasized workflow instead of business logic. Views were simpler. That stuff was put in the models. Model code was easier to reuse.</p><p>Thus arose the great <a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">“God Model”</a> issue. Fat is one thing, but we had some seriously obese models. Things like User and Task simply had too much going on. We could put stuff in mixins/concerns but that didn’t change the fact that there was tons of code that all could be subtly interacting with each other.</p><p>Business logic has to go somewhere. For us, that somewhere is in service objects.</p><h3>Operations</h3><p>In our architecture, we call them “Operations” and they extend a class called Backend::Op. This more or less uses the <a href="https://github.com/mnelson/subroutine">subroutine</a> gem.</p><p>Much can be read about what it means to be a service object, but here is my very scientific (Rails-specific) definition.</p><ul><li>Includes ActiveModel stuff like Naming, Validations, and Callbacks</li><li>Allows declaration of what fields (input parameters) it uses</li><li>Reflects an action in the system like “sign up a user” or “invoice a job”</li><li>Does whatever it needs to do to accomplish the action when asked including updating or creating one or more models</li></ul><p>Here’s a simplified example:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a2645f5e831f22870adae768850826a4/href">https://medium.com/media/a2645f5e831f22870adae768850826a4/href</a></iframe><h3>No Side Effects</h3><p>When we followed the “Fat Model” pattern, we got what we wanted. This was usually methods in one of the models. Sometimes there were callbacks added. These were the most dangerous because they were happening on every save. Often, this added unnecessary side effects.</p><p>With the service object approach, it is very clear what is happening for the action at hand. When you “invoice a job,” you create the invoice, generate the payment, mark the appointment done, schedule the next appointment, and enqueue some background workers.</p><p>This certainty leads to less technical and product debt. When something new needs to be added to this action, it’s very clear where it goes.</p><h3>Errors</h3><p>Our Op class above does several model manipulations to the related invoices, appointments, etc. Each some of these does a save to something. Those save calls could raise errors. If any of those raise an error, then the Op itself will inherit it and it will be available on the op.errors method just like a normal ActiveRecord object.</p><p>This also allows chaining of operations. If there was a ScheduleAppointmentOp class, it could be used in the above schedule_next_appointment! method. If it raised an error, it would propagate to the InvoiceJobOp.</p><h3>Controllers</h3><p>Generally speaking, we have one Op per controller action that declares what it expects and manipulates the backend data as needed.</p><p>Here is a typical example from one of our controllers.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3bf69b890748661ecabb0c2106f8d20d/href">https://medium.com/media/3bf69b890748661ecabb0c2106f8d20d/href</a></iframe><p>An action will typically do the following:</p><ul><li>Load a resource</li><li>Authorize the user is allowed do do an action</li><li>Perform the action with an operation (other things are in place to render and error if the op fails)</li><li>Render a template</li></ul><p>Note that this is clearly not a typical RESTful route. We’ve found that becomes less important when using this pattern. When the controllers are just wiring things up and are all a 5 lines or less, it feels like there is more flexibility.</p><p>It probably gets summed up something like this: wherever the fat (real work) is, that should be focused. For us, it’s not the controller because of service objects. The real work is 1 to 1 focused with the use case. If more was in the controllers, we’d probably be closer to the standard index, show, etc methods because of the focus concept.</p><h3>Sharing</h3><p>So we have pushed everything out closer to the user experience and away from the models. But what if something is needed in a few pieces of the experience?</p><p>A few ways we have done sharing:</p><ul><li>Two Ops can use a lower-level one or other type of class as noted above.</li><li>Two Ops can have a mixin with the shared behavior.</li><li>We can add a method to an applicable model. We tend to do this on simple methods that are interpreting the model data to answer a commonly-asked question or commonly-used display value.</li></ul><h3>Summary</h3><p>We have found that this approach provides a more maintainable and overall successful way of building Rails apps.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f936fecf142" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/architecture-service-objects-4f936fecf142">Architecture: Service Objects</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Architecture: Models]]></title>
            <link>https://medium.com/bl-blog/architecture-models-a9fb75bf35fd?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/a9fb75bf35fd</guid>
            <category><![CDATA[ruby-on-rails]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Fri, 24 Feb 2017 08:00:00 GMT</pubDate>
            <atom:updated>2017-02-26T06:16:09.399Z</atom:updated>
            <content:encoded><![CDATA[<p>This is the first post in what I hope will be a series of articles about how we build things at <a href="https://www.taskrabbit.com/">TaskRabbit</a>. Over time, we/I have internalized all kinds of lessons and patterns, but have never written them down explicitly and publicly. So let’s give that a try.</p><p>I thought we’d start with models. That’s what <a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html">Rails</a> calls database tables where each row is an instance of that model class.</p><p>Overall, we default to the following rules when designing the models in a system:</p><ul><li>Keep the scope small and based on decisions in the workflow</li><li>Use state machines to declare and lock in the valid transitions</li><li>Denormalize as needed to optimize use cases in the experience</li></ul><h4>Scope</h4><p>When designing a feature (or the whole app in its early days), you have to decide what the models represent. I’m calling that the “scope” of the model.</p><p>For example, most applications have a User model. What columns will it have? Stuff about the user, obviously. But what stuff? One of the tradeoffs to consider is User vs. Account vs. Profile. If you put everything about the user in the same table as the one that’s pointed to in many foreign keys through the system, there will be a performance impact.</p><p>So we put the most commonly needed items on every screen load in the User model and “extra” stuff in the Profile.</p><ul><li>User: authentication, name, avatar, state</li><li>Profile: address, average rating, bio information</li></ul><p>There are plenty of ways to cut this up into other models and move things around, but that’s what I mean about “scope” of a model.</p><h4>States</h4><p>State machines are built into the foundation of the system. Almost every model has a state column and an initial state. There are then valid transitions to other states.</p><p>For example, there is a PaymentTransaction model. It has an initial “pending” state that represents the time between when an invoice is submitted and when we charge the credit card. During this time, it can move to a “canceled” state if tit should not happen. Or, if things go as planned, it can transition to a “settled” state. After that, if there is an issue of some sort, it would go to a “refunded” state. Notably, going from “pending” to “refunded” is <em>not</em> a valid transition.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/566/0*MpruZjhCZ1hc0NqX.png" /></figure><p>Creating these state and transitions preserves some sanity in the system. It’s a safety check. By asserting what is possible, we can (try to) prevent things that should not be possible.</p><h4>Nouns and Verbs</h4><p>The TaskRabbit marketplace creates a job that is sent to a Tasker. The Tasker can chat with the Client and can say they will do the job. Or they can decline. If they agree, they are officially assigned to the job and make an appointment. When they complete the job, they invoice the Client for the time worked. In most cases, it’s done at that point. In other cases, it is “ongoing” where they come back next week (to clean again, for example). At more or less any time, they whole thing can be canceled.</p><p>If given that description, you could come up with many possible model structures. They would all have a set of pros and cons, but many would work out just fine.</p><p>For example, you could have a Job model with these kinds of states: invited, invitation_declined, assigned, appointment_made, invoiced, invoice_paid, canceled, etc. Each would only allow the valid transitions as described above. You would also need the columns to represent the data: client_id, tasker_id, appointment_at, etc.</p><p>The main benefit of this approach is centrality. You can SELECT * FROM jobs WHERE client_id = 42 and get all of that user’s situation. Over time, however, we came to value a more decentralized approach.</p><p>Now, the models of our system reflect its objects and decisions that the actors make about them. Each fork in the experience has a corresponding model with a simple state machine.</p><p>For example, the Invitation model is created first to note the decision the Tasker must make. It then either transitions to accepted or declined. If accepted, it spawns an Assignment. It, in turn, can move to states like completed or ongoing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/361/0*gusae-iioqsvBpLL.png" /></figure><p>There is still the the Job model but it contains the “description” of the work to do and its id ties together the decision-based models.</p><h4>Trade-offs</h4><p>Everything is pros and cons. The decentralized approach has more global complexity (more objects and interactions) but less local complexity (simpler decisions, states).</p><p>It seemed to be the single, monolithic state machine that doomed the single Job model. Everything is fine as long as that’s the only path through the system. However, as soon as there is a new way for a Task to be assigned, we have a tangled web of states.</p><p>Not every task has the invitation pattern noted above. Some are “broadcast” to many Taskers at once and shown in a browse-able “Available Tasks” section in the their app. That’s a new fork in the experience. Ongoing tasks also create a state loop of sorts.</p><p>These cause the single state machine to get a bit tangled up, but is more easily handled in the decentralized approach. We can make a Broadcast model instead of an Invitation one. That can have its own set of states. Success in that local state machine can also spawn an Assignment and everything goes on as before.</p><h4>Denormalization</h4><p>To try and get the both worlds, we have also aggressively embraced a variety of forms of denormalization.</p><p>We actively try not to do SQL JOINs for simplicity and performance reasons, but that is at odds with all these little models all over the place. So we have said it’s OK to have duplicate data. For example, each of these “decision” models have the client_id, tasker_id, and pricing information. It just gets passed along. This makes everything a local decision and queries very straightforward.</p><p>The big hole in the decentralized approach is to “get all my stuff” easily. For that we have different tactics, both of which are denormalization with use cases in mind.</p><p>On write to an object, we can update a central model with the current situation for that Job. For example, when an Assignment gets created, we recalculate and store data in two different tables. One for both the Tasker and the Client on what they should be seeing on their respective dashboards. Thus, the API call to “get all my stuff” uses one of those tables. That is done in the same transaction as the original write.</p><p>The other option is basically the same thing but for either less time-sensitive data or more complicated queries. We use a <a href="https://medium.com/blog/2015/04/02/queue-bus/">message bus</a> to observe changes. We then denormalize applicable data for a specific use case into a table or <a href="http://www.elastic.co/">Elasticsearch</a>. For example, when an Appointment is created, we would update the Taskers availability schedule in the database. Updating this schedule would also trigger an update to our recommendation algorithm which uses Elasticsearch.</p><p>One important note: all of these denormalizations should be <a href="http://www.restapitutorial.com/lessons/idempotency.html">idempotent</a>. This allows us to recreate the whole thing from the source of truth or recover if any given event is dropped.</p><h4>Summary</h4><p>At TaskRabbit, we default to the following rules when designing the models in a system:</p><ul><li>Keep the scope small and based on decisions in the workflow</li><li>Use state machines to declare and lock in the valid transitions</li><li>Denormalize as needed to optimize use cases in the experience</li></ul><p>As always, these are just the default guidelines. In any given case, there may be a reason to deviate, but it would have to be clear why that case was special.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9fb75bf35fd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/architecture-models-a9fb75bf35fd">Architecture: Models</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Developing an Amazon Alexa Skill on Rails]]></title>
            <link>https://medium.com/bl-blog/developing-an-amazon-alexa-skill-on-rails-52951ea2018a?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/52951ea2018a</guid>
            <category><![CDATA[ruby-on-rails]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[bots]]></category>
            <category><![CDATA[alexa]]></category>
            <category><![CDATA[voices]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Fri, 02 Dec 2016 06:00:00 GMT</pubDate>
            <atom:updated>2016-12-26T22:23:06.133Z</atom:updated>
            <content:encoded><![CDATA[<p>In March, we had a hack day at <a href="https://www.taskrabbit.com/">TaskRabbit</a> and I did a demo of posting a task using a borrowed new-ish (at the time) Amazon <a href="https://www.amazon.com/echo">Echo</a> via <a href="https://developer.amazon.com/alexa">Alexa</a>. For the first time in a year, I made a new <a href="https://medium.com/blog/2014/02/11/rails-4-engines/">engine</a> that would handle all these new-fangled conversational UIs and bots and stuff.</p><p>The hack day came and went (I didn’t win) and this branch was just sitting there every time I did a git branch command. I only have a few there. Keep it clean, people! Then I saw the Cyber Monday deals on Amazon. I decided that it had sat there long enough so I dusted it off to try and bring it to the finish line.</p><p>I more or less started over, of course, because that’s how it goes. I thought I would document the process for anyone else on the trail.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/0*m0bmT9FvSCoOk7rZ.png" /></figure><h4>Alexa Sessions</h4><p>The <a href="https://developer.amazon.com/alexa-skills-kit">Alexa API</a> uses JSON to make requests and receive responses. Each session has a guid and (optional) user information.</p><p>The API has some cool session management tricks. You can <a href="https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference">return attributes</a> that will also get passed back on the next request. This effectively gives you “memory” of the previous parts of a conversation. I chose to not do this because I am hoping to use the same engine for other similiar interfaces. Instead I save the same stuff but to a table table using the session guid as the key. In ether case, it’s important to know where you’ve been and what you need to move forward.</p><p>In our case, we want to check the box that says there has to be a linked user. Because this is checked, the Alexa App will send them through an OAuth flow on our site. So we generate a token that maps to the user in our system and Alexa stores that token in hers. Side note: it’s hard to not fully personify Alexa after talking (arguing) back and forth all week.</p><h4>Hello World</h4><p>Alexa is given a single endpoint for a skill. It will POST the request to that route. So I added the line to the routes.rb file and sent it to a new SkillsController. It looks something like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/17874f1ae8abd25c6f40db833a7e79bc/href">https://medium.com/media/17874f1ae8abd25c6f40db833a7e79bc/href</a></iframe><p>I used the <a href="https://github.com/damianFC/alexa-rubykit">alexa_rubykit</a> gem with some <a href="https://github.com/damianFC/alexa-rubykit/pull/5">modifications</a> to parse the request and write the response.</p><p>So how can we get the Echo on the desk to talk to the computer? It’s only 12 inches away and yet… so far! The Alexa app in the developer console has to point to a publically accessible HTTPS site. I googled around a little bit and stumbled upon <a href="https://ngrok.com/">ngrok</a>. You install ngrok and run ngrok http 3000. This gives you a public https site that forwards to your localhost that you can put in the developer console.</p><h4>Alexa Intents</h4><p>To know what the user said involves the <a href="https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interaction-model-reference">intents</a> that are created in the developer console.</p><p>A simple example to get whatever the user said would look like this.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/24ef88d4790883a3b333fe4d33a9b53e/href">https://medium.com/media/24ef88d4790883a3b333fe4d33a9b53e/href</a></iframe><p>You also use “utterances” to give examples of this generic input.</p><p>There are also several other helpful intents that normalize data. For example, the user can say the date and time in many ways but Amazon can normalize that and send over a known format. Other examples include commands commands like <em>yes</em>, <em>no</em>, <em>cancel</em>, and <em>stop</em>.</p><p>Here are the intents I ended up with:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/dc1708029c514e4d18f33ae94e433e4c/href">https://medium.com/media/dc1708029c514e4d18f33ae94e433e4c/href</a></iframe><p>I used the <a href="https://github.com/sidoh/alexa_generator">alexa_generator</a> gem with some <a href="https://github.com/sidoh/alexa_generator/pull/1">updates</a> to declare these in a way that looks like routes. It also allows you to give examples which helps generate all the files that is needed.</p><p>For example, here is my alexa.rb file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/15c505e3aa0576ae39e5c1e3e193384e/href">https://medium.com/media/15c505e3aa0576ae39e5c1e3e193384e/href</a></iframe><p>Running a rake job I wrote will the generate the above intents json as well as the sample utterances for the developer console.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d645cc38b95f500e1f6d0b0f4e87ec6d/href">https://medium.com/media/d645cc38b95f500e1f6d0b0f4e87ec6d/href</a></iframe><h4>Simple Response</h4><p>A simple skill would probably have one-ish intent and few examples. It would receives those in the controller, return the response, and then end the session. We would also handle a few of the states to help the user out.</p><p>The controller might look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/420b1e91b2de4fc9343cf87e31662913/href">https://medium.com/media/420b1e91b2de4fc9343cf87e31662913/href</a></iframe><h4>Conversations</h4><p>It all gets a bit more complicated when there is a back and forth conversation. At this point, I would say Alexa is not yet optimized for this use case.</p><p>For example, in our app with the shown set of intents, any one of them could come through. I could ask the user a yes/no question like “Your task is ready to book. Continue?” but the user could say “clean my house” or literally… anything. So I’d be expecting a AMAZON.YesIntent but get a AMAZON.Literal one. At the same time, it’s very helpful to use the built in intents for their normalization capabilities. Otherwise, I’d have to do my own natural language stuff to know all the variations of dates and ways to cancel, etc.</p><p>So the trick of a conversation seems to be to know the state, know the related intents that are expected, and merge them together as best as possible. As noted, I store the state and the data collected in the database. In concept (in reality this is spread out over many classes), we add a case statement to the controller.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/13408974e21a25be7e34171470ceaba5/href">https://medium.com/media/13408974e21a25be7e34171470ceaba5/href</a></iframe><p>Using this pattern, you can have a decent conversation.</p><h4>SDK Update Requests</h4><p>There are two simple things that I think would make this a much better platform.</p><p>The first is to be able to handle conversations better. If I could include which intents I am expecting back from the thing I just asked, everything would be 10x better.</p><p>The issue can be seen when the app asks for more details about the app. Basically, it wants wants to get a AMAZON.Literal of a few sentences and write it down. I found that if the user happens to say “tomorrow” in there somewhere, it sometimes matches the Date and that’s the only data I get.</p><p>The issue is that what I’m interested in is specified globally and therefore does not have the context. If we could respond with expected intents or something to that effect, conversations would be much better.</p><p>The other feature is to be able to return links in the card. When I return LinkAccount card in a response, there is a call to action on the card in the Alexa App to do OAuth. I would like to return text and URL to put arbitrary things in the same spot. That way I could link the user to their task they just posted to create a more seamless experience.</p><h4>Summary</h4><p>Alexa development is fairly straightforward assuming you don’t need or already have the OAuth provider bits set up. Most of the docs talk about a Java package but doing it in the Rails environment was no trouble with existing gems or parsing the json yourself.</p><p>It’s not quite as easy for conversations but you can make it work. A few more tweaks, along with push notifications, would add a ton of value.</p><p>The TaskRabbit Skill is now published! Check it out.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=52951ea2018a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/developing-an-amazon-alexa-skill-on-rails-52951ea2018a">Developing an Amazon Alexa Skill on Rails</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Post Election]]></title>
            <link>https://medium.com/bl-blog/post-election-d94be19a536b?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/d94be19a536b</guid>
            <category><![CDATA[business]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[politics]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Thu, 10 Nov 2016 06:00:00 GMT</pubDate>
            <atom:updated>2016-12-26T03:02:02.823Z</atom:updated>
            <content:encoded><![CDATA[<p>I’ve been thinking a lot about the book <a href="https://en.wikipedia.org/wiki/The_City_%26_the_City"><em>The City &amp; the City</em></a> by China Miéville. It describes a town in which two sets of people share the same physical space but do not acknowledge each other. For that matter, they are forbidden to do so.</p><p>I remember being surprised in 1992 when Bill Clinton won the election. I lived in Texas and everyone I knew was voting for Bush. Now, I live in California and the same thing snuck up on me again this week. There’s very little learning there.</p><p>But it’s not just where I live because technology has allowed a nation like in the book. Today, my Twitter feed is filled with anxiety, sadness, outrage, and very scared people. I am certain there are people nearby, not to mention in all those (many) red states, that have an exceptionally different feed: one full of hope, expectation, and triumph. And there is no connection between the two.</p><p>In that 1992 election, it was the economy (<a href="https://en.wikipedia.org/wiki/It%27s_the_economy,_stupid">stupid</a>) and the need for change. I’m certainly not a political analyst, but that rings just as true this week. All the jobs numbers are up over the last 8 years, but not everywhere and not for everyone. This has caused a rift.</p><h4>Forward</h4><p>Where do we go from here? I believe it’s best for each of use to use our talents and position as leverage to make a difference. In my case, I can help literally provide work in these areas of the country.</p><p><a href="https://www.taskrabbit.com/">TaskRabbit</a> has been focusing on its largest markets because there is still plenty of room to grow there. And the whole thing is a hard problem. The focus helps, but our map is somewhat bare in Middle America. For me, this election is a kick in the pants to get there sooner than later.</p><p>The biggest fail there would be to believe that we can “save” people from on high by bestowing the magic of technology. Fortunately, even as the chief technologist at TaskRabbit, I understand that’s not where the value lies. It’s always been about neighbors helping neighbors. We’re just there to make the real-life connection.</p><p>I don’t know about you, but I think we could all really use a few more real-life connections at the moment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d94be19a536b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/post-election-d94be19a536b">Post Election</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native Android Launch]]></title>
            <link>https://medium.com/bl-blog/react-native-android-launch-754f018f9385?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/754f018f9385</guid>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Thu, 24 Mar 2016 05:00:00 GMT</pubDate>
            <atom:updated>2016-12-26T22:26:00.552Z</atom:updated>
            <content:encoded><![CDATA[<p>Yesterday, we launched our updated Tasker app to our Android community. As noted <a href="https://medium.com/blog/2015/12/17/react-native-launch/">before</a> on the iOS launch, this is the app that Taskers use to get their work done. This completes our migration to <a href="https://facebook.github.io/react-native/">React Native</a>.</p><p>All of the credit goes to the team that made this happen, especially <a href="https://twitter.com/jrichardlai">JR</a> and <a href="http://jeremyeaton.co/">Jeremy</a>. It was a lot harder than expected to get everything working on both platforms and they showed great dedication and persistence.</p><h4>Approach</h4><p>The goal of the last release was for the iOS users to not even notice. Mission (more or less) accomplished! However, for this one, it didn’t make sense to fork the code by platform without a good reason. So admittedly, the app looks more like an iOS app and than an Android one. However, we did go screen by screen looking for places where Android-specific attention would help the user.</p><h4>Differences</h4><p>I’ll let JR do a followup to his <a href="https://tech.taskrabbit.com/blog/2016/01/11/react-native-android/">previous</a> post of all the differences, but the biggest ones in my mind were the handling of the hardware back button and different pickers (date, for example) in the forms. The Platform directory strategy we had put in place during the first cycle worked out pretty well.</p><p>We also really struggled with getting push notifications right. This is key for our business and there were many more nuances on the Android platform to work out. We hope to publish what we came up with.</p><p>Of course, there was also the more extensive testing to do. Our Android pile:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*fqs2z_gBVgg-yoMR.jpg" /></figure><h4>Stats</h4><ul><li>App Javascript: 302 files with 21515 lines of code</li><li>Test Javascript: 47 files with 5708 lines of code</li><li>iOS Javascript: 19 files with 449 lines of code</li><li>Android Javascript: 19 files with 770 lines of code</li><li>Objective C: 17 files with 885 lines of code</li><li>Java: 15 files with 912 lines of code</li><li>iOS Config files: 18 files with 2538 lines of stuff</li><li>Android Config files: 16 files with 1106 lines of stuff</li><li>React Components: 124</li><li>Screens (addressable url patterns): 25</li><li>Avg. components per screen: 5</li><li>Dispatcher Events: 55</li><li>Shared JS (vs. Platform JS) percentage: 94%</li><li>JS (vs. Native ObjC/Java) percentage: 92%</li><li>Total shared code percentage: 87%</li><li>Total shared (including config) percentage: 75%</li></ul><h4>Next steps</h4><p>Now, we certainly didn’t do this engineering project because the tech was cool (even though it is). We did it to create a foundation that allows us to deliver value more effectively to our community. So that’s the next order of business. Time to get rolling! I estimate that we can ship features to both platforms at least twice as quickly with half the engineers than we had before.</p><p>Too long, but did read anyway: For you execs out there that somehow read this far (even past lines of code counts!), I’d say that we’ve found React Native to be at least 5x more productive than traditional mobile development.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=754f018f9385" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/react-native-android-launch-754f018f9385">React Native Android Launch</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native Launch]]></title>
            <link>https://medium.com/bl-blog/react-native-launch-3cac039328c0?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/3cac039328c0</guid>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Thu, 17 Dec 2015 06:00:00 GMT</pubDate>
            <atom:updated>2016-12-26T22:26:49.449Z</atom:updated>
            <content:encoded><![CDATA[<p>This week, we launched our updated Tasker app to the community. This is not the app on the app store, but rather the one the Taskers use to get their work done. Functionally, not much has changed since the last release. But underneath, the app has been completely rewritten in <a href="https://facebook.github.io/react-native/">React Native</a>.</p><p>First and foremost, a huge congratulations needs to go out to the team, especially <a href="https://twitter.com/jrichardlai">JR</a>. I’ve never been on an engineering project that went as smoothly or was as fun as this one. So good!</p><h4>Prototype</h4><p>I started looking into React Native in the beginning of August. It’s a really great story. First of all, it’s <a href="https://facebook.github.io/react/">React</a>. I’ve now decided that’s just about the best thing out there. Secondly, I was 10x more productive than when developing a regular iOS app. Finally, there was a decent chance much of the code could be reused on Android. If every feature did not have to be developed twice, we could choose to develop twice as many features or have half the engineers work on something else.</p><p>I created a prototype to see if I thought it could work. The main concerns I had we around navigation, push notifications, other native features, data storage, and just getting the right project structure. More or less, I ended up with the <a href="https://github.com/taskrabbit/ReactNativeSampleApp">React Native Sample App</a> that we published. All of those things showed great promise or at least that they were possible.</p><p>The community was also evolving rapidly. It’s super exciting to be working at the beginning of a technology that shows great promise. Each week, there would be a new thing on github that solved one of our needs. Our strategy was to put off a feature to see if the core team or community would make something available. If they did not, we went ahead and <a href="https://github.com/taskrabbit/react-native-parsed-text">wrote</a> it. I’m sure everyone was doing something similar. Let’s call it bootstrap chicken.</p><p>The prototype was enough to convince me and enough to get people excited about it. Towards the end of September, JR started working full-time on it. My time in greenfield projects had to come to a close. Back to real life, but I stayed involved as the Product Manager.</p><h4>Product Management</h4><p>I can’t imagine anyone saying that I’m a very good PM. In particular, my story acceptance followup is horrendous. Fortunately, <a href="https://twitter.com/pdevine">Paul</a> helped me out. I did do a pretty good job of writing the stories and putting them in the right order.</p><p>One decision made this project work, though. It was to acknowledge that this is an engineering project, not a product one. It’s so very tempting to update the functionality of the app “while you’re at it.” But opening that door would have doubled the length of the project. Those discussions with design and others would eat up the development time. It would have made the rollout harder. We could do a soft release because it’s basically the same app. I’ve written about <a href="https://medium.com/blog/2015/10/26/rewrite/">rewrites</a> before, but that was to drastically change the product. If you can get away with it, do it in place and keep it the same. At least we followed one of those.</p><p>So I dragged stories around while JR worked his butt off. This is not a small app and we hit critical functional parity (with many UI tweaks needed) at the beginning of October. We put this in the hands of some Taskers and got their feedback. From there it was getting the less critical features done, getting feedback, shipping new builds, making it prettier, and fixing bugs.</p><h4>Shipping</h4><p>By last week, we felt really good about it but needed to buckle down to get the release out. We’d been working so quickly that it was hard to slow down and be more methodical in the final run up. We felt good because <a href="http://jeremyeaton.co/">Jeremy</a> joined the team, we had great <a href="https://medium.com/blog/2015/11/08/react-native-integration-tests/">test coverage</a>, and real Taskers in multiple countries were actually using the thing.</p><p>We put out another release to a wider audience and incorporated a few final items. Then… ship it. The reception has been neutral. Nothing good or bad. This is amazing! That means success in a technology project with no new features except all of the old ones.</p><h4>Notes</h4><p>I just wanted to note a few interesting things that we did or encountered.</p><ul><li>We have been beta testing React Native integration from <a href="https://realm.io/">Realm</a>. All of our lists (non-singletons) stores are backed by a Realm database. For example, we use this for storing tasks. The various lists and drill-down screens all use the same data. That means its very likely to be always up to date. If you’re interested in trying it out, sign up <a href="http://goo.gl/forms/UvS8ZM9LOA">here</a>.</li><li>Be careful about the JS runtime (iOS version). We’ve been testing in iOS 9. Right before release, we realized a library we use was using new Symbol(). It works in 9.0+ but not in 8.x versions of the OS.</li><li>Our navigation scheme really is awesome. It makes everything easier. Check out more information <a href="https://medium.com/blog/2015/09/21/react-native-example-app/">here</a>.</li><li>Internationalization was really interesting. I’ll write up more on this topic but, briefly: each component declares its own words and we have code to go through all the components and collect them up. Then we can send that off for translation.</li><li><a href="https://facebook.github.io/react/docs/flux-overview.html">Flux</a> and event dispatching in general really worked out well. Just having minor piece of indirection reduces coupling and leads to great results</li><li>JR is trying to convert me to ES2015 and also away from <a href="https://facebook.github.io/react/docs/reusable-components.html#mixins">mixins</a>. He did this <a href="https://github.com/taskrabbit/react-component-extension">extension</a> thing. He’s probably right.</li><li>I think there’s a missing pattern for how to run things in the “background” in these apps. For example, maybe we’re supposed to sync something with the server every hour or so. We made something up but I haven’t seen anything else about it.</li><li>Everything that is iOS specific is in one directory right now and it’s not much. I’m really looking forward to the Android build.</li></ul><h4>Stats</h4><ul><li>Javascript: 351 files with 37168 lines of code</li><li>Objective C: 12 files with 1029 lines of code</li><li>React Components: 112</li><li>Screens (addressable url patterns): 25</li><li>Dispatcher Events: 52</li><li>Platform (iOS specific): 18 files with 605 lines of code</li><li>JS / C percentage: 97%</li><li>Shareable code percentage: 93% (so far — we’ll see with Android)</li><li>Avg. components per screen: 4.5</li></ul><h4>Next steps</h4><p>Now we are going to fix up a few minor things and on to Android. How exciting!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3cac039328c0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/react-native-launch-3cac039328c0">React Native Launch</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native Integration Tests]]></title>
            <link>https://medium.com/bl-blog/react-native-integration-tests-22e2620a71b9?source=rss----b4434f474a15---4</link>
            <guid isPermaLink="false">https://medium.com/p/22e2620a71b9</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[taskrabbit]]></category>
            <category><![CDATA[appium]]></category>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Brian Leonard]]></dc:creator>
            <pubDate>Sun, 08 Nov 2015 06:00:00 GMT</pubDate>
            <atom:updated>2016-12-26T22:29:53.129Z</atom:updated>
            <content:encoded><![CDATA[<p>Coming from a Rails <a href="http://guides.rubyonrails.org/testing.html">background</a>, we are very familiar with testing our code. While writing our new <a href="https://medium.com/blog/2015/09/21/react-native-example-app/">React Native app</a>, we found ourself missing a way to test it and ship with confidence. I’ve updated the <a href="https://github.com/taskrabbit/ReactNativeSampleApp">sample app</a> with the approach we are using for integration tests.</p><h4>Test Levels</h4><p>When thinking about what and how to test a React Native app, a few levels come to mind:</p><ul><li>Unit: Testing some pure Javascript object and it’s methods. Just run in Javascript.</li><li>Component: Testing a React component in isolation. You’d want to check its reaction to various state and props. Maybe run just in Javascript with heavy stubbing or in the simulator.</li><li>Integration: Testing a single screen or workflow in the “real” app. Run in the simulator or on the device.</li></ul><p>The approach shown here is the last one: integration testing. We did this one first because if you are only going to do one of the above, it is probably your best bet. By actually testing out what the user does, you get the highest level of “don’t screw it up” coverage.</p><p>There are some tradeoffs in this choice. They mostly stem from the fact that it’s the slowest (runtime) approach. Because of that, to test many edges cases takes f-o-r-e-v-e-r to actually run the tests. Something lower-level without the simulator would be much faster.</p><h4>Running Tests</h4><p>In the <a href="https://github.com/taskrabbit/ReactNativeSampleApp">sample app</a>, you follow these steps:</p><ul><li>Make sure you have the 9.0 simulators installed in XCode</li><li>Compile app for the test environment: npm run compile:test</li><li>Launch simulator and tests: npm test</li></ul><p>Running npm test will launch the simulator and the robots take over.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/911/0*73ABRGyZWkaEJIuu.gif" /></figure><h4>Plumbing</h4><p>The tests are written in Javascript using the <a href="https://mochajs.org/">mocha</a> testing framework. This allows you to declare your cases much like <a href="http://rspec.info/">rspec</a> does in Ruby. It gives you hooks to run things before and after as well.</p><p>The simulator is run and controlled using <a href="http://appium.io/">Appium</a>. This is some serious magic that implements the <a href="http://www.seleniumhq.org/">Selenium</a> web testing framework but for iOS and Android.</p><p>The compile step is important because it compiles the iOS code with a slightly different environment.</p><p>The environment lets the iOS code knows to talk to a different port to get it’s code. The React Native <a href="https://github.com/facebook/react-native/tree/master/packager">packager</a> needs to be running on that port so the test suite launches it automatically. This means you don’t have to recompile the jsbundle each time you make a JS change — just like on the simulator with Command+R. In the same way, this is a tremendous improvement to the development process.</p><p>The environment also lets the the JS code do a few things differently. For example, it talks to a different localhost API port than the development app. The test suite launches a small <a href="http://koajs.com/#introduction">koa</a> server on that port. This allows any given test to specify exactly what the server should return for any given API call so we can test the app in a known state.</p><p>When in test mode, the <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/App/Root.js#L87">Root</a> component also adds in a <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/App/Root/TestRunner.js#L19">TestRunner</a> component at the top. But giving Appium buttons, it allows the suite to reset the test each time and bootstrap the app. It gets it’s bootstrap commands from the koa server. It also hooks logging so that all the console.log calls are sent to the koa server so everything can be logged in the test terminal. In both these, it’s the koa server that is the “bridge” between the tests and the simulator.</p><p>Putting it all together:</p><ul><li>npm test runs mocha</li><li>mocha <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/test/helpers/driver.js">spawns</a> an Appium driver process (which launches the simulator)</li><li>mocha <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/test/helpers/packager.js">spawns</a> a React Native Packager process</li><li>mocha <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/test/helpers/server.js">spawns</a> a koa server process</li><li>the test starts</li><li>the test clicks “ResetTest” to be sure to start over (which sends a message to the iOS code to delete some documents)</li><li>the test clicks “Bootstrap” to get setup instructions.</li><li>the test uses the Appium API (driver.findElementById(‘Log in’).click()) to do stuff and see how it goes</li></ul><h4>Bootstrap</h4><p>The concept of “Bootstrap” might warrant a little more explanation.</p><p>The sample app has been set up to use the <a href="https://facebook.github.io/react/docs/flux-overview.html">flux pattern</a> and <a href="https://medium.com/blog/2015/09/21/react-native-example-app/">url-based routing</a>. These two things allow a test to put the world in the state it needs to check the behavior.</p><p>When writing Appium tests on our Objective-C app, to test an acount management feature, it would log in, tap the sidebar, tap the gear, then tap “Change Password” or whatever. Then the test really starts. This time really adds up.</p><p>It gets easier with flux and routes. For example, because everything in our React Native app is based on events from the dispatcher, we don’t have to <em>actually</em> log in. We can just dispatch the LOGIN_USER action with the right properties.</p><p>Then, because it’s based on URLs, we don’t have to navigate to the spot we want. We can just dispatch the LAUNCH_ROUTE_PATH with the appropriate URL and go right to the screen under test.</p><p>All of this saves a tremendous amount of time and headache when things change.</p><p>So how does this work? The test says what it wants to do to set up the world. That might look like this: yield bootstrap().login().nav(“dashboard/follows/friend”).launch(driver);. This registers actions (login, navigate) with the koa server.</p><p>Then when the test clicks “Bootstrap” on the app, it fetches a particular url form the koa server to get its intructions. In this case, it will dispatch the LOGIN_USER action, followed by the LAUNCH_ROUTE_PATH action. Any arbitrary action could be set, but these are the most common.</p><h4>Example</h4><p>This is what it looks like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/032658b69ba6817351fe295050eff8df/href">https://medium.com/media/032658b69ba6817351fe295050eff8df/href</a></iframe><p>Here it is running:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/911/0*XEB6un7AzU5YG-ty.gif" /></figure><p>I find all the yield stuff kind of annoying but it also prevents the <a href="http://www.joshwright.com/tips/javascript-christmas-trees-promises-and-event-emitters">christmas tree of doom</a> situation.</p><h4>Test On!</h4><p>Now that we’ve written the integration tests, we can run them on a CI service like <a href="https://travis-ci.org/">Travis</a>.</p><p>I set up a <a href="https://github.com/taskrabbit/ReactNativeSampleApp/blob/9863a1037b2eb2259518c62d37cd50840790e099/.travis.yml">.travis.yml</a> file in the sample app and it’s <a href="https://travis-ci.org/taskrabbit/ReactNativeSampleApp">green</a>!</p><p>Well, that’s it. This is a very new space and we didn’t see a great way out there to do this kind of testing in a painless-as-possible kind of way. Hopefully, what we’ve done here can be useful for you in your own journey towards React Native apps that work as expected.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=22e2620a71b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bl-blog/react-native-integration-tests-22e2620a71b9">React Native Integration Tests</a> was originally published in <a href="https://medium.com/bl-blog">BL Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>