<?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[Motiv Engineering - Medium]]></title>
        <description><![CDATA[Stories and lessons learned on engineering and startups - Medium]]></description>
        <link>https://medium.com/motiv-engineering-blog?source=rss----427c0b6ef0fd---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Motiv Engineering - Medium</title>
            <link>https://medium.com/motiv-engineering-blog?source=rss----427c0b6ef0fd---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 24 Apr 2026 09:55:17 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/motiv-engineering-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[Reducing time to release: minimizing the manual Test Cycle]]></title>
            <link>https://medium.com/motiv-engineering-blog/reducing-time-to-release-minimizing-the-manual-test-cycle-9fcaae4e4968?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/9fcaae4e4968</guid>
            <category><![CDATA[qa]]></category>
            <category><![CDATA[quality-assurance]]></category>
            <category><![CDATA[release-management]]></category>
            <dc:creator><![CDATA[Kristina Tuchkova]]></dc:creator>
            <pubDate>Thu, 06 Sep 2018 18:27:26 GMT</pubDate>
            <atom:updated>2018-09-06T17:43:53.841Z</atom:updated>
            <content:encoded><![CDATA[<p>Testing is a very important part of the Software Development Lifecycle. There are a lot of basic guidelines on how to organize the testing cycle, but it is usually up to the team to develop the most efficient testing strategy and deliver results in a timely manner.</p><p>Quality Assurance is composed of a complex set of steps, which could be divided into four main parts: Analyzing, Planning, Testing, and Reporting. I would like to highlight “Planning”, as this step paves the path for the whole testing process. In order to plan a test cycle, one of the most time-saving and guiding instruments is a test case. Well formulated test cases have a great impact on the testing phase and writing them is as important as testing itself. Test cases are usually based on PRDs, design specs, user experience, and flow charts. Each feature could be separated into positive and negative flows to cover as many edge cases as possible.</p><p>As the application grows and the list of product functionalities expands, the test case tree expands as well. Eventually, it becomes very complex. This is a milestone that should be achieved, but, at the same time, it comes with a pretty big downside: along with the test case tree, the release cycle expands and the time for regression testing significantly increases, which leads to a delay in getting testing feedback and shortens the time frame for potential issue resolution.</p><p>So how do you maintain a high quality of testing in constrained time, if automation is still under development? How do you minimize the release cycle and simultaneously not miss out on anything important?</p><ol><li><strong>Reduce the number of test cases in each testing suite by omitting low-risk scenarios</strong></li></ol><p>It’s good practice to assign the “type” to each test case. The most commonly used types are Sanity, Smoke, Functional and Regression. This approach helps to group the test cases into test suites and execute them based on priority. The testing process that I find most efficient is executed in the following order:</p><ul><li>Smoke &amp; Sanity test suite (includes the most high-level scenarios and high-risk tests)</li><li>Functional &amp; Regression test suite (includes the main functionality of the application and regressions)</li><li>Sanity test suite (includes only high-level scenarios, as this is the final check before the release)</li></ul><p>Test cycle time can be shortened by reducing the number of test cases in each of the testing suites. It can be done by adding one more type called “Monthly”, the main purpose of which is to bundle all the test cases that won’t block the release if their status is “Failed” and execute them once a month separately from the release Cycle. Having fewer test cases in each test suite should significantly speed up the testing process while ensuring quality.</p><p>The test case tree needs to be closely monitored for changes in the product requirements and their corresponding impact on the functionality of the application. Adjustments should be done regularly in order to keep the process up-to-date.</p><p><strong>2. Determine the combination of test devices and environments based on priority</strong></p><p>The important thing to keep in mind is the variety of devices and environments on the market. For example, on the web, customers access the product in any number of browsers and browser versions. Also, there is a large variety of mobile devices, platforms and operating system combinations to consider. Comprehensive coverage means that the release cycle would be endless and inefficient.</p><p>Tools like Mixpanel and Google Analytics are used by QA Engineers to analyze the summary of devices customers use the most and set the priorities on what to test first based on customers’ impact.</p><p>If testing with a lot of different combinations is critical and necessary, the other useful approach would be to complete the Smoke &amp; Sanity test suite by testing on the wider variety of devices or browsers and then move to the Functional &amp; Regression test suite by testing only on the primary cohort. It should catch the device-specific or environment-specific issues in the early stage of the release cycle.</p><p><strong>3. Pre-release testing process: Minimize the QA on RC build</strong></p><p>Pre-release cycle testing helps to discover issues early in the process. The goal is to test new features along with the high-risk areas and provide the detailed feedback accordingly. Also, it is important to keep the queue of tickets which are ready for QA verification clear prior to the release cycle.</p><p>This approach significantly reduces the chances of critical issues slipping into the release candidate build and leads to a clean and blocker-free release cycle.</p><p><strong>4. Use resources efficiently and remove the duplication of efforts</strong></p><p>This method is largely related to efficient teamwork. It is very important to divide the work for the release between the QA Engineers since the more eyes there will be on the product, the fewer defects could end up in the released build.</p><p>A simple trick to try and avoid duplication of efforts in the early stage of the testing process is executing a different sequence of test cases at a given time. As an example, one team member could start from the bottom of the test suite and another one from the top. This approach will ensure that a potentially critical issue will be found way faster and the results will be provided in a timely manner.</p><p><strong>Conclusion</strong></p><p>The key benefit of minimizing the time of release cycle for QA Engineers is the additional time gained that can be directed towards the completion of other testing activities, thereby increasing the overall quality of the product, improving test documentation and expanding test coverage. The ability to manage the release cycle and keep the process up-to-date is a valuable skill set for any QA Engineer.</p><p>These methods allow Motiv to deliver more value to customers faster as well as save a lot of time for the company. Additionally, it significantly improved the performance of the test team and boosted confidence in a successful product launch.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9fcaae4e4968" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/reducing-time-to-release-minimizing-the-manual-test-cycle-9fcaae4e4968">Reducing time to release: minimizing the manual Test Cycle</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing PMVP, a Reactive Mobile Application Design Pattern]]></title>
            <link>https://medium.com/motiv-engineering-blog/introducing-pmvp-a-reactive-mobile-application-design-pattern-b2216b34ec0c?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/b2216b34ec0c</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[design-patterns]]></category>
            <dc:creator><![CDATA[Aubrey Goodman]]></dc:creator>
            <pubDate>Sun, 26 Aug 2018 02:06:30 GMT</pubDate>
            <atom:updated>2018-08-26T02:06:29.578Z</atom:updated>
            <content:encoded><![CDATA[<p>I know what you’re thinking. Another design pattern?! Your company has been using Pattern XYZ since they started building mobile apps. It has worked just fine so far, except all those bugs along the way. Why take the time to re-train all your engineers to use a new tool? Well, sit back and I’ll tell you.</p><p>It starts with acronyms.</p><p>When I started iOS development in 2008, the best practice was something called Model-View-Controller (MVC). This was one of the first patterns to make a tsunami-sized wave in the global community. Before its adoption in iOS, MVC was the standard for desktop application design. It enabled the separation of critical aspects of the application lifecycle into discrete components whose responsibilities were clear. And there was much rejoicing.</p><p>By this time, Javascript was beginning to experience a huge resurgence in the web development community as a server-side language. The last ten years have been an extended golden age for Javascript, with new open source libraries popping up all over the place. During this same period, web browser users grew increasingly demanding of responsive features and reactive components. This ushered in the new hotness, called Model-View-ViewModel (MVVM).</p><p>With MVVM, components started reacting in a more fluid way to upstream data changes. With an in-memory store of a data model managing the state of your view hierarchy, browsers could offer unprecedented control to developers. There was suddenly an object in browser memory with all the properties of the view state <strong>based on</strong> the data model state. This allowed UI developers to build reactive components, where pending changes were assumed to succeed until the response data returned from the remote server provided new information. In this way, reactive design was born.</p><p>Drunk on its own euphoric rise to prominence, the MVVM community forked, and a new faction emerged to champion their new pattern, called Model-View-Presenter (MVP). As an engineer building systems with these patterns, I’ve had a hard time keeping them separate. Candidly, when MVP came along, I found myself wondering why it is any different from MVVM or MVC. At their core, the most innovative aspect is the isolation of key behaviors into discrete components with clear responsibilities. The specific nature of the delegation of responsibility seemed situational.</p><p>Then, a critical milestone was reached — Reactive Extensions (Rx) were introduced to the developer community. Before Rx was published, multi-threaded systems required ubiquitous consideration for execution context and tools to enable dispatched code to be executed on a specific queue, using some thread-safe mechanism. It was extremely difficult to find engineers with advanced skill in this area. Many excellent best practices emerged for managing task execution in a multi-threaded environment. With the advent of Rx, all that was turned on its head. Rx enables the development of synchronous multi-threaded systems whose components react to external influence. Moreover, Rx enforces a strict declarative approach to system behavior definition. In other words, reactive systems follow pre-defined pathways, triggered by specific notifications.</p><p>“When X happens, perform task Y.”</p><p>This is highly counterintuitive to industry veterans. Some of us have spent the last 20 years building things that work in a very different way, like this:</p><p>“Every N seconds, check the value of X asynchronously and conditionally perform task Y when X satisfies some criteria.”</p><p>Hilariously, even the English sentence required to describe the behavior is longer, just as the non-Rx code is typically more verbose than its Rx equivalent. Let that sink in…</p><p>Here at Motiv, we have encountered some very complex problems related to real-time data processing and we believe there is a better way than all of the patterns described above. We have devised a pattern — taking advantage of the Rx philosophy — to enable an unprecedented level of clarity in reactive design. We call this Provider-Model-View-Presenter (PMVP).</p><h3>Overview</h3><h4>Provider</h4><p>The Motiv ecosystem introduced a new component into the pattern. The Provider component is responsible for owning all lifecycle context for a group of data models. In its simplest form, you might think of it as a template, like Provider&lt;T&gt;, where T is the model type. This is highly flexible. You might have Provider&lt;Array&lt;T&gt;&gt; or Provider&lt;Map&lt;K,T&gt;&gt; or any arbitrarily complex data type. The key is the Provider object owns the lifecycle of all the objects it manages. We found the name “provider” the most universally clear and concise term. There have been flame wars across the internet about words like “manager” and “controller.” Adding fuel to the fire, the word “service” has special meaning on Android.</p><h4>Model</h4><p>In this context, Model refers to the ViewModel. The Provider owns the data model, so we can decouple our Model as a dedicated component responsible for view state. I want to be clear about one thing here. “View state” means the state of view components, but not anything like “label6 is visible.” If you envision the view components as being participants in a story, the Model is the storyteller.</p><h4>View</h4><p>In PMVP, the View components represent the standard elements used to compose a user interface, such as labels, images, etc. These components have no interactive behaviors associated with them. They are purely driven by presenters.</p><h4>Presenter</h4><p>In essence, the Presenter is responsible for updating objects in the view hierarchy. For simple views, this may require only one presenter. For more complex views, it may make more sense to separate groups of view objects into multiple presenters. Also, every Presenter must only be made aware of the ViewModel. In this way, Presenter is always a slave to ViewModel state. Presenters can only subscribe to observables exposed by the ViewModel. By definition, they shall never subscribe to external components; these subscriptions shall be defined in the ViewModel and exposed as derivative observables, if necessary. This guarantees the ViewModel is the ultimate source of truth for all behaviors defined in the UI layer.</p><h3>PMVP in Action, a Tale of Two Implementations</h3><p>Now that you’ve had an overview, let’s look at a real world example. We’re going to build a simple interface two different ways and compare them. This helps to highlight the benefits of using PMVP over traditional techniques. For our example, we will build a tamagotchi simulator. Let’s dive right in!</p><h4>Requirements</h4><p>Tamagotchi simulate the needs of a living creature as follows:</p><ul><li>Model shall track food as an integer property.</li><li>Food shall decrease by one unit per tick.</li></ul><p>Non-interactive components:</p><ul><li>Creature shall emote “HUNGRY” each tick when food is low (&lt;10).</li><li>Creature shall emote “DEAD” when food supply equals zero.</li></ul><p>Interactive components:</p><ul><li>User can tap FEED button to add ten units of food, but not if creature is dead.</li></ul><p>Initial conditions:</p><ul><li>Creature starts with 5 food.</li></ul><p>View hierarchy:</p><pre>view<br>- creatureImageView<br>- creatureStateLabel<br>- feedButton</pre><h3>Part 1: Traditional Approach</h3><p>Using traditional approach, we would create a view controller to represent our tamagotchi state. We track the creature food state as a simple integer property on the view controller itself. Then we create a tick method to update the data model, and we invoke the tick method using a timer. This forces us to define logic in the tick method to handle the state of UI elements. It also forces our tick method to be invoked on main queue (because NSTimer needs a run loop to function properly).</p><pre>class TamagotchiViewController: UIViewController {</pre><pre>  @IBOutlet private weak var creatureImageView: UIImageView!<br>  @IBOutlet private weak var creatureStateLabel: UILabel!<br>  @IBOutlet private weak var feedButton: UIButton!</pre><pre>  private var food: Int = 20</pre><pre>  override func viewDidLoad() {<br>    super.viewDidLoad()<br>    creatureStateLabel.isHidden = true<br>  }</pre><pre>  private func tick() {<br>    if food == 1 {<br>      food = 0<br>      emoteDead()<br>    }<br>    else {<br>      food -= 1<br>      if food &lt; 10 {<br>        emoteHungry()<br>      }<br>    }<br>  }</pre><pre>  @IBAction func feed() {<br>    food += 10<br>  }<br>  <br>  private func emoteHungry() {<br>    creatureStateLabel.text = “HUNGRY”<br>    creatureStateLabel.isHidden = false<br>  }</pre><pre>  private func emoteDead() {<br>    creatureStateLabel.text = “DEAD”<br>    creatureStateLabel.isHidden = false<br>  }<br>}</pre><h3>Part 2: PMVP Approach</h3><p>With PMVP, we introduce a view model and presenter to separate the responsibilities into discrete components. The food tracking properties move into the view model. Also note the view model exposes observables related to the UI components. Instead of publishing a “food” observable and forcing the presenter to define the “hungry” and “dead” states, we define them explicitly in the view model.</p><h3>Provider</h3><p>With PMVP, we must have a component responsible for managing the state of the data model. In this case, we define a CreatureProvider, which owns a Creature model and manages its lifecycle. Note: both public methods include a guard against invalid state; they only emit new state if the current state is valid.</p><pre>struct Creature {<br>  var food: Int<br>}</pre><pre>class CreatureProvider {<br><br>  let creatureSubject = BehaviorSubject&lt;Creature&gt;(value: Creature(food: 5))</pre><pre>  private let disposable: Disposable</pre><pre>  init() {<br>    disposable = Observer&lt;Int&gt;.interval(1.0, scheduler: MainScheduler.asyncInstance)<br>      .subscribe(onNext: { [weak self] _ in self?.tick() })<br>  }</pre><pre>  func feed() {<br>    guard var creature = try? creatureSubject.value(), creature.food != 0 else { return }<br>    creature.food += 10<br>    creatureSubject.onNext(creature)<br>  }</pre><pre>  private func tick() {<br>    guard var creature = try? creatureSubject.value(), creature.food &gt; 0 else { return }<br>    creature.food -= 1<br>    creatureSubject.onNext(creature)<br>  }<br>}</pre><h3>View Controller</h3><p>The PMVP view controller has very similar structure to its traditional counterpart. It is responsible for owning references to all the view elements, as well as the model and presenter(s). Also, any interaction logic lives here. Note the inclusion of the tap observer, which delegates to the model.</p><pre>class TamagotchiViewController: UIViewController {</pre><pre>  @IBOutlet private weak var creatureImageView: UIImageView!<br>  @IBOutlet private weak var creatureStateLabel: UILabel!<br>  @IBOutlet private weak var feedButton: UIButton!</pre><pre>  private let viewModel = TamagotchiViewModel()<br>  private var presenter: TamagotchiPresenter!<br>  private var disposeBag = DisposeBag()</pre><pre>  override func viewDidLoad() {<br>    super.viewDidLoad()<br>    presenter = TamagotchiPresenter(viewModel: viewModel,<br>      imageView: creatureImageView,<br>      label: creatureStateLabel,<br>      button: feedButton)<br>  }</pre><pre>  override func viewWillAppear() {<br>    super.viewWillAppear()<br>    registerObservers()<br>    presenter.registerObservers()<br>  }</pre><pre>  override func viewWillDisappear() {<br>    super.viewWillDisappear()<br>    disposeObservers()<br>    presenter.disposeObservers()<br>  }</pre><pre>  private func registerObservers() {<br>    feedButton.rx.tap<br>      .subscribe(onNext: { [weak self] _ in<br>        self?.viewModel.feed()<br>      })<br>      .disposed(by: disposeBag)<br>  }</pre><pre>  private func disposeObservers() {<br>    disposeBag = DisposeBag()<br>  }<br>}</pre><h3>Model</h3><p>Now, the logic for tracking creature state is fully encapsulated into the provider. Changes to the creature data model are published via observable. The view model subscribes to the provider observable and reacts to changes. In this case, the view model tracks the creature’s food directly, binding it to a local BehaviorSubject. Furthermore, the model now also publishes observables specifically intended to drive behavior in the presenter.</p><pre>class TamagotchiViewModel {<br>  private let foodSubject = BehaviorSubject&lt;Int&gt;(value: 5)<br>  private let disposeBag = DisposeBag()</pre><pre>  init() {<br>    CreatureProvider.instance.creature()<br>      .map({ $0.food })<br>      .observeOn(MainScheduler.asyncInstance)<br>      .bind(to: foodSubject)<br>      .disposed(by: disposeBag)<br>  }</pre><pre>  func hungry() -&gt; Observable&lt;Bool&gt; {<br>    return foodSubject.map({ $0 &lt; 10 })<br>  }</pre><pre>  func dead() -&gt; Observable&lt;Bool&gt; {<br>    return foodSubject.map({ $0 == 0 })<br>  }</pre><pre>  func feed() {<br>    CreatureProvider.instance.feed()<br>  }<br>}</pre><h3>Presenter</h3><p>This class is responsible for reacting to state changes in the model and updating view elements accordingly. Note: there is no interaction logic being defined here. The observer subscriptions are simple content bindings. All interaction observers are defined in the controller.</p><pre>class TamagotchiPresenter {<br>  private let viewModel: TamagotchiViewModel<br>  private let imageView: UIImageView<br>  private let label: UILabel<br>  private let button: UIButton<br>  private var disposeBag = DisposeBag()</pre><pre>  init(viewModel: TamagotchiViewModel, imageView: UIImageView, label: UILabel, button: UIButton) {<br>    self.viewModel = viewModel<br>    self.imageView = imageView<br>    self.label = label<br>    self.button = button<br>  }</pre><pre>  func registerObservers() {<br>    Observable.combineLatest(viewModel.hungry(), viewModel.dead())<br>      .map({ !($0 || $1) })<br>      .bind(to: label.rx.isHidden)<br>      .disposed(by: disposeBag)</pre><pre>    viewModel.hungry()<br>      .filter({ $0 })<br>      .map({ “Hungry” })<br>      .bind(to: label.rx.text)<br>      .disposed(by: disposeBag)</pre><pre>    viewModel.dead()<br>      .filter({ $0 })<br>      .map({ “Dead” })<br>      .bind(to: label.rx.text)<br>      .disposed(by: disposeBag)</pre><pre>    viewModel.dead()<br>      .bind(to: button.rx.enabled)<br>      .disposed(by: disposeBag)<br>  }</pre><pre>  func disposeObservers() {<br>    disposeBag = DisposeBag()<br>  }<br>}</pre><h3>Comparison</h3><h4>Cons</h4><ul><li><strong>Rx Required.</strong> Using this pattern will require your engineers to learn Rx.</li><li><strong>Steep learning curve.</strong> While is it incredibly powerful, reactive design is very different from traditional techniques to software design. There are many rough edges, pitfalls, and gotchas, and it can be challenging to identify the source of strange behaviors. Learning Rx can be especially challenging for industry veterans, who must adjust to a “new normal” and think about things backwards and upside down.</li><li><strong>Verbose.</strong> For very simple examples like the one given above, PMVP feels like a little overkill. We must add two new classes, and each class has its own boilerplate requirements for managing disposable lifecycle.</li></ul><h4>Pros</h4><ul><li><strong>Reactive. </strong>The declarative nature of Rx leads to highly readable code, acting more like a recipe than a procedural sequence of commands. Instead of using method invocations to fetch state from external sources, the pattern is inverted; we have observers receiving new values and responding with specific behavior.</li><li><strong>Team-friendly.</strong> The decoupled component architecture enforces a natural integration boundary between components. This makes it easier to delegate development responsibility to team members. One person can build a view model, while another builds a presenter. Moreover, different functional areas within a complex UI can be split up into multiple presenters — one for each area — and built by different team members.</li><li><strong>Easy to review.</strong> Our engineers celebrate the elegance and simplicity introduced into our code review process. PMVP changes are easy to understand in context and decrease the cognitive load on the reviewers.</li><li><strong>Testable!</strong> The introduction of Rx-based view model and presenter make it easy to test the interaction between data and UI layers. Providers can be mocked into test fixtures to exercise the view model observable behavior in response to changes published upstream. This makes it easier to incorporate unit tests into your UI code.</li></ul><h3>Conclusion</h3><p>We’re really excited about our new pattern, and we’re using it in all future versions of the Motiv app. It truly crystallizes development of robust components by forcing developers to focus on critical aspects of a design before building anything. It demands a consideration for state machine dynamics and compels engineers to define all the external triggers causing state transition. This helps your team members break down complex interactions into something simple and easy-to-understand.</p><p>We couldn’t be happier about PMVP, and we look forward to sharing more articles and screencasts over the next year!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b2216b34ec0c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/introducing-pmvp-a-reactive-mobile-application-design-pattern-b2216b34ec0c">Introducing PMVP, a Reactive Mobile Application Design Pattern</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Book review: “Measure What Matters”]]></title>
            <link>https://medium.com/motiv-engineering-blog/book-review-measure-what-matters-61d73b3645f0?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/61d73b3645f0</guid>
            <category><![CDATA[book-review]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[management]]></category>
            <category><![CDATA[okr]]></category>
            <dc:creator><![CDATA[Dan Post]]></dc:creator>
            <pubDate>Tue, 12 Jun 2018 06:41:41 GMT</pubDate>
            <atom:updated>2018-06-12T06:40:02.538Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Some light vacation reading</strong></p><p>Every time I take more than a few days of vacation, I bring at least one book for professional development. (And try to finish reading it before I binge on sci-fi and beaches.) This time, I thought I’d share my learnings with you.</p><p>For this trip, I chose “<a href="https://www.whatmatters.com/">Measure What Matters</a>,” a recently released book by John Doerr, the influential venture capitalist and godfather to many successful silicon valley companies. “Measure” introduces the OKR system, a management tool for aligning, inspiring and stretching organizations to peak performance and unity of action.</p><p>The tagline is catchy: “How Google, Bono, and the Gates Foundation Rock the World with OKRs.” Wouldn’t you like your team to have the impact those groups have had on the world?</p><p><strong>What’s in it?</strong></p><p>The book is split into three main sections, each of which comprise several chapters, weaving stories of how organizations have implemented OKRs to great success, along with principles and lessons.</p><p>OKRs stand for “objectives and key results.” Objectives are your organization’s near-term goals to push you towards a vision, with key results being the indisputable yardstick of achievement.</p><p>The main principles, or superpowers, covered between the stories are: focus and commitment to priorities, align and connect for teamwork, track for accountability, and stretch goals.</p><p>There are two chapters on another useful and related management tool: CFRs, or conversations, feedback and recognition. This is worthy of its own book (even though much has been written on the topic), and I’m glad “Measure” introduced it and tied it in with the OKR system.</p><p><strong>What it means to me</strong></p><p>I was first introduced to OKRs during my time at Intel, where some parts of the organization used MBOs in classic Drucker fashion, and others used OKRs, which owe their genesis to Andy Grove. (There are several important distinctions which I’ll completely skip over.) Reading “Measure” gave me a deeper understanding of the principles and benefits of the OKR system.</p><p>“Measure” also subtly interweaves an important set of concepts: input, activity, output, outcome, and impact (see <a href="https://en.wikipedia.org/wiki/Logic_model">logic model</a>). The best OKRs measure outcome and/or impact (settle for output if you have to), since inputs and activities can just waste resources (the “activity trap”). Input and activity goals are easy to avoid with a little bit of effort (and “Measure” has some practical tips).</p><p>Going from output- to outcome/impact-oriented can be challenging in particular for engineering teams, since we usually focus on completing projects within the context of known cross-functional programs and organizational goals, and take great pride in shipping. OKRs for maintaining infrastructure and product quality metrics are usually easier to frame. Practically, your OKRs will probably include a mix, but whenever possible, phrase the goal in terms of the impact you’re looking to get from achieving it, and you will measure up to your full potential.</p><p>I wish I had access to this book when I took on my first leadership role about a decade ago. The combination of narrative examples and principles created a powerful learning experience that I hope to draw on in my work. Go read it, or learn more at <a href="http://whatmatters.com">whatmatters.com</a>, and let’s compare notes.</p><p>If you read it carefully and reflect, you’ll get a practical guideline to introducing structured goal setting in any organization to get more better results per unit of effort, along with some inspirational stories that convey nuances and options you have to customize it. If you’re already doing OKRs, read the book and re-review your current set of OKRs; you’ll probably see a lot of room for improvement. (I did!)</p><p><strong>OKRs at Motiv</strong></p><p>At Motiv, we use the OKR system (with a right-sized tool for our company size: a Google Sheet). It’s provided a level of transparency, accountability, and alignment that otherwise gets lost when you grow beyond a small team.</p><p>If you are interested in rocking the world with us and using OKRs, check out our <a href="https://mymotiv.com/careers/">careers page</a> — we’re hiring!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=61d73b3645f0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/book-review-measure-what-matters-61d73b3645f0">Book review: “Measure What Matters”</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Natural Language Processing Techniques to Identify iOS App Eviction]]></title>
            <link>https://medium.com/motiv-engineering-blog/using-natural-language-processing-techniques-to-identify-ios-app-eviction-aecf91802290?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/aecf91802290</guid>
            <category><![CDATA[natural-language-process]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[mobile-analytics]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <dc:creator><![CDATA[Aubrey Goodman]]></dc:creator>
            <pubDate>Thu, 10 May 2018 21:27:12 GMT</pubDate>
            <atom:updated>2018-05-10T21:32:41.243Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Nerdiness</strong>: 5/5</p><p><strong>Topics</strong>: iOS, Mobile Analytics, SQL, NLP, n-grams</p><p>Apps running in the background on iOS are subject to a set of rules, defined by Apple. In some circumstances, the operating system will force a backgrounded app to be terminated, usually to free up resources for an active app. The specific details of when this happens are not published by Apple. They simply advise developers to be aware their apps may be evicted in this way. In this scenario, the app lifecycle methods are not invoked, so there is no convenient callback we can use to log the eviction. We may receive a memory warning callback, but even that is not guaranteed. Instead, we must rely on our data warehouse and logging infrastructure to provide critical information regarding the state of the app in the wild.</p><h3>Prerequisites</h3><p>Before we can discuss the approach in depth, we need a clear picture of the tools we’ll need to achieve our goal.</p><h3>Distributed Logging</h3><p>This approach is predicated on existing logging infrastructure. We use Amazon Redshift to aggregate logging data. Our logger works based on unique strings representing significant events within the app. We use multiple logging levels to categorize logs, but for the purposes of this piece, we’re focused on the “info” log level, which has the following method signature:</p><pre>protocol Logger {<br>  class func info(_ name: String)<br>}</pre><p>Each time this method is invoked, we effectively add a new row in a Redshift table. This table is queryable via SQL. This is important because we’ll be using some advanced SQL techniques later.</p><h3>App Lifecycle Logging</h3><p>At an absolute minimum, we need to track the lifecycle of our app in the wild. To do this, we annotate our AppDelegate with launch and termination logs, like this:</p><pre>func applicationDidFinishLaunching(…) {<br>  Logger.info(“app.launched”)<br>}</pre><pre>func applicationWillTerminate(…) {<br>  Logger.info(“app.terminated”)<br>}</pre><p>Of course, other areas of your app will need to have similar logging for this approach to be useful. As long as you include logging for your critical systems, you can probably use this technique.</p><h3>Methodology</h3><p>Zooming out a bit, app logging manifests as a collection of streams, one for each user. We can take advantage of this naturally sequential phenomenon to study the flow of execution. If we include sufficient logging in the various components of our system, we can highlight sub-sequences where the normal flow is truncated. These represent moments within a user flow where the user encountered a crash or eviction. We can use SQL tools to identify and characterize these scenarios. Consider the following sequence:</p><pre>app.launched<br>app.fetch_started<br>app.fetch_completed<br>ui.refresh<br>app.backgrounded<br>app.background_update_started<br>app.background_update_completed<br>app.foregrounded<br>ui.refresh<br>app.terminated</pre><p>This simple example shows a sequence we might expect to loop over time, as the user interacts with their app. As long as the user remains active, this sequence or one similar to it will appear in the data warehouse, when querying for the given user and ordering by timestamp. If this stream is interrupted by a crash or eviction, we will abruptly see the first message in the sequence, in this case app.launched immediately preceded by a message other than app.terminated in the sequence. The following example shows a truncated sequence:</p><pre>app.background_update_started<br>app.launched</pre><p>In this instance, the last log we see before the app.launched indicates the app was processing a background update before it was unexpectedly killed. We can learn quite a lot from analyzing sequences of logs in context with each other. These anomalies can help guide in diagnostics. This can help avoid costly exploration and/or logic audit by prioritizing high-value options first.</p><h3>Natural Language Processing with N-Grams</h3><p>There is a rich set of tools available for analyzing sequences. Here, we will employ a powerful tool from natural language processing (NLP) to organize the logging data in our warehouse. Log data is a time-ordered sequence of “words,” represented by individual log messages. In NLP parlance, these are known as n-grams, or a sequence of n words in a row. For example, a trigram is a sequence of three words in a row, like “take the bus.” If we map this technique to our logging data, we will find lots of unique n-gram sequences. Filtering these n-grams down to those ending with the app.launched log, we can characterize the scenarios leading to early termination. This analysis results in a report showing the most popular eviction n-grams.</p><p>For our purposes, we will focus on trigrams. The general outline can be extended to support sequences of any arbitrary number. Let’s dig into the SQL query details.</p><h3>SQL Lead/Lag</h3><p>One very powerful suite of SQL functions makes this problem very easy to solve. Most SQL functions produce results for records matching conditional criteria. This is great for known conditions, but it doesn’t tell you anything about the context of the rows relative to the other rows. With LEAD/LAG, we can select adjacent rows and include them in the context of their neighboring rows. Here’s an example of a query to identify the number of rows matching our first log:</p><pre>select<br>event_type,<br>count(*)<br>from logs<br>where event_type = ‘app.launched’<br>group by event_type</pre><p>This query will count all instances in our data warehouse with the matching event type, which is very unhelpful for our purposes. However, adding just a little extra to this query will add substantial value. Here’s an expanded example:</p><pre>select<br>event_type,<br>lag(event_type)<br>  over (partition by user order by event_timestamp)<br>  as previous_type<br>from logs<br>where event_type = ‘app.launched’</pre><p>This will result in a list of all combinations of app.launched and their preceding log. It’s important to note the options in the OVER clause. These determine the constraints of the LEAD/LAG function. If we do not include the user partitioning, the results will be nonsense, as they will include records from potentially different users. By partitioning, we’re instructing the database to include results where the adjacent records are related by a common user. Now we have a tool to find all the instances of termination, but we’re not quite done. We need one more thing to support trigrams.</p><p>The LEAD/LAG functions work beyond the adjacent rows. They also support an offset parameter. Using this parameter, we can specify an arbitrary offset from the given reference row. This allows us to construct a simple query to organize our trigrams.</p><pre>select<br>event_type as e0,<br>lag(event_type, 1)<br>  over (partition by user order by event_timestamp)<br>  as e1,<br>lag(event_type, 2)<br>  over (partition by user order by event_timestamp)<br>  as e2<br>from logs<br>where event_type = ‘app.launched’</pre><p>Now we have a result set with e0,e1,e2 columns and rows representing all combinations of three events leading up to app launch. This collection includes the normal termination scenarios as well, since they also culminate in app launch. We need to wrap this in a second query to filter these out. This looks something like the following:</p><pre>select<br>e0,e1<br>from (…)<br>where e1 != ‘app.terminated’</pre><p>Finally, we modify this to include consideration for statistics, so we can order our results. Observant readers will note we’ve removed the e2 parameter from the result set, as it will always be app.launched. This simplifies the query and allows us to use the GROUP BY feature, resulting in the following:</p><pre>select<br>e0, e1, count(*)<br>from (…)<br>where e1 != ‘app.terminated’<br>group by e0, e1<br>order by count desc</pre><p>Now, we have a result set showing each eviction trigram and its corresponding frequency. The resulting table is immediately actionable, with the top offenders presented nicely along with their individual significance. The results might look something like this:</p><pre>e0,               e1,                            count<br>app.backgrounded  app.background_update_started  987<br>app.backgrounded  ui.refresh                     658<br>app.foregrounded  app.backgrounded               276</pre><p>In this case, the chief offender is a stalled background update; a process starts but never finishes. Next appears to be the dreaded “UI update on background queue” causing the app to use too many resources on a main queue while in a background state.</p><h3>Accelerated Diagnostics</h3><p>Using these techniques, you can begin to explore some of the undiscovered sharp edges of your user experience. This targeted approach allows engineers to reduce the total time spent solving hard problems. This shines a spotlight on a traditionally dark and devious area of multithreaded systems — deadlocks. When your app deadlocks in the wild, most likely the operating system will simply evict it in the background. The user may not notice, especially if your app refreshes its state on becoming active. This means thankfully no users report problems, but it also means tragically no users report problems, so the problems go unreported. By applying some creative multidisciplinary thinking, using simple tools, we can achieve a powerful result, reducing time to resolve challenging bugs.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aecf91802290" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/using-natural-language-processing-techniques-to-identify-ios-app-eviction-aecf91802290">Using Natural Language Processing Techniques to Identify iOS App Eviction</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[App Zombies: Feeding the iOS Deadlock]]></title>
            <link>https://medium.com/motiv-engineering-blog/app-zombies-feeding-the-ios-deadlock-8ae84834d4dd?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/8ae84834d4dd</guid>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[multithreading]]></category>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Dan Post]]></dc:creator>
            <pubDate>Thu, 26 Apr 2018 07:18:58 GMT</pubDate>
            <atom:updated>2018-04-26T07:17:28.730Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Hard things are hard</strong></h3><p>Prepare yourself. This is one of the biggest horrors of Motiv app engineers, the bane of our existence:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*8IwlN0fk6zGREYTC4XuYsQ.png" /><figcaption>The app, not being useful.</figcaption></figure><p>We call this the “infinite spinner.” That, to be fair, is technically incorrect, as none of us have waited past the heat death of the universe to see if it resolves. It’s much more likely to be starvation, deadlock, or livelock.</p><p>We <strong>almost never </strong>see this issue. That doesn’t negate the user pain of having to wait, then decide it’s not going to fix itself, kill the app, launch it (waiting for the OS to completely evict the prior process), wait for it to load, and manually sync the ring and wait for the data to transfer and process, since the starvation probably prevented automatic background syncs from occurring.</p><p>This leads us to problem two: since the user kills the app, and it is not detected by a crash reporter, we have zero visibility into the frequency or root cause of this problem. Zilch. Nada. No visibility means prioritizing it and debugging it is impossible!</p><p>But wait. I have a screenshot of the behavior, and know that it happened. It turns out, seeing this on an alpha build and knowing what to do are actually sufficient! We don’t have to know the size of the iceberg at first, just knowing that there is one helps us steer around it and bring other specialty tools to bear. Let’s look at two different doors.</p><h3><strong>Door 0: Avoid the problem</strong></h3><p>Some architectures lend themselves to reducing certain classes of human errors.</p><p>For example, typed languages catch at compile time many types of bugs that would be runtime bugs in dynamic languages. Some environments may offer <a href="https://clang.llvm.org/docs/ThreadSafetyAnalysis.html">thread safety analysis</a>.</p><p>However, exhaustively checking that all possibility of these class of problems has been eradicated is most likely equivalent to the halting problem, so it’s computationally infeasible to prove. Additionally, rewriting an entire system to solve one bug is a tough and highly speculative investment to make. (What if it doesn’t solve the problem?) So let’s leave this door closed for now.</p><h3><strong>Door 1: Sysdiagnose</strong></h3><p>There is a very useful, but little known, system feature in iOS called sysdiagnose. You trigger it on recent devices by pressing both volume buttons and the home button at the same time. Your phone should vibrate, indicating sysdiagnose has done its job. There are <a href="https://download.developer.apple.com/iOS/iOS_Logs/sysdiagnose_Logging_Instructions.pdf">articles</a> on <a href="https://www.jessesquires.com/blog/how-to-sysdiagnose-ios/">how to use sysdiagnose</a>, but nothing about how to get value out of it as a developer.</p><p>Sysdiagnose writes some files named stacks-YYYY-MM-DD-TTTTTT.ips. Once you sync with iTunes, you’ll find them in ~/Library/Logs/CrashReporter/MobileDevice/DEVICENAME/. These files contain a record of every single process in the system, each thread for each process, and each stack frame in each process! Sounds useful.</p><p>Here’s the downside: they only contain program counter offsets. Useless!</p><p>However, if you’ve been a good iOS engineer, you’ve archived all of your dSYMs from every alpha and external build somewhere permanent (in our case, in an S3 bucket).</p><p>Enter <em>symbolicate-stackshot</em>: a simple Ruby command-line utility to transmogrify your stack file plus the dSYM into something human-readable! Now, visibility!</p><p>Here is one of several threads stuck in similar places:</p><blockquote>*******************************************************************</blockquote><blockquote>Process 1150</blockquote><blockquote>========================================</blockquote><blockquote>Thread 843020</blockquote><blockquote>0x000000010097d7c4 (in Motiv)</blockquote><blockquote>0x0000000100802498 (in Motiv)</blockquote><blockquote>0x0000000100802e88 (in Motiv)</blockquote><blockquote>0x00000001007f4b44 (in Motiv)</blockquote><blockquote>1836ceb4c</blockquote><blockquote>@objc ReadableMotivModelContext.performAtomicallySerially(readBlock:) (in Motiv) (ReadableMotivModelContext.swift:0)</blockquote><blockquote>-[MotivModelController performReadOnly:] (in Motiv) (MotivModelController.m:214)</blockquote><blockquote>+[MVDailyTargetService weeklyTargetForWeekContaingDate:forType:] (in Motiv) (MVDailyTargetService.m:81)</blockquote><p>In our case, the primary failure revealed an issue with our CoreData setup that caused starvation of the original main thread and temporary saturation of background threads. Embarrassingly, it also caused several operations that should have been durable/synchronous database updates to be asynchronous.</p><p>From the commit message from one of our developers:</p><p><em>A simple execution of the following code would cause a deadlock</em></p><blockquote>for _ in 0…50 {</blockquote><blockquote>Dispatch.global().async {</blockquote><blockquote>MotivModelController.instance.performAtomically { (context) in</blockquote><blockquote>//insert some object</blockquote><blockquote>}</blockquote><blockquote>}</blockquote><blockquote>}</blockquote><p>The awkwardly small diff:</p><blockquote>- parentContext.perform {</blockquote><blockquote>+ parentContext.performAndWait {</blockquote><h3><strong>Door 2: Cerberus, the watchdog</strong></h3><p>The venerable watchdog is old hat to embedded programmers. The concept is simple: part of your system (usually strictly separated from the other parts of your system) keeps time, and when it reaches a predetermined time value, it resets the system. When your system is operating normally, it “kicks” the watchdog. If something prevents normal flow through, your system automatically recovers.</p><p>The watchdog is usually implemented in hardware, though some systems have software watchdogs. Hybrid systems can be powerful; a software watchdog, triggered by a timer in ISR context, can save diagnostic info when it works so you have information to debug from, and a hardware watchdog is your safety net from total and complete lockup. But I digress.</p><p>Let’s apply the watchdog concept to a mobile app.</p><p>For complex or top-level user operations (example: taps on ‘week’ view), we could start an NSThread that sleeps, then logs or crashes. Completion of the regular code path (data loading and refreshing the screen) would then cancel the watchdog thread.</p><p>We have selectively deployed a limited version of a watchdog that only logs initially, as it will bring visibility to problems we may otherwise only occasionally hear a non-actionable (no data to debug) report from a customer.</p><p>What to do if you make the watchdog too sensitive, and your users experience crashes far too frequently? Consider having a server-controlled parameter (feature flag) that switches the watchdog between logging and crashing. Also consider setting the timeout relatively high, as your users may navigate faster through your app than you anticipated or tested for, leading to a backlog of operations to complete!</p><h3><strong>Wrapping up</strong></h3><p>Check out our basic sysdiagnose stackshot symbolicator <a href="https://github.com/postster/symbolicate-stackshot">over here</a> on GitHub. Pull requests welcome!</p><p>I hope you’ve learned a bit more about failure modes of multithreaded programs, and now have some tools for diagnosing or working around some of the most difficult class of bugs.</p><p>Motiv is hiring — if you want to work with us, check out our <a href="https://mymotiv.com/careers/">careers page</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ae84834d4dd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/app-zombies-feeding-the-ios-deadlock-8ae84834d4dd">App Zombies: Feeding the iOS Deadlock</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Effective Unit Tests for Time Travel]]></title>
            <link>https://medium.com/motiv-engineering-blog/effective-unit-tests-for-time-travel-1dda984187a0?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/1dda984187a0</guid>
            <category><![CDATA[time]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[tips]]></category>
            <category><![CDATA[unit-testing]]></category>
            <dc:creator><![CDATA[Aubrey Goodman]]></dc:creator>
            <pubDate>Fri, 23 Mar 2018 04:49:42 GMT</pubDate>
            <atom:updated>2018-03-23T04:49:42.353Z</atom:updated>
            <content:encoded><![CDATA[<p>At Motiv, we strive to do things others have deemed impossible. We’ve done some amazing work to cram a lot of sophisticated sensor equipment and processing capability into a ring. Motiv Ring does imbue the wearer with special powers, but not the time travelling kind. While we can’t make any claims of time travel, we have seen some strange things in our system logs related to time. I’d like to share some of those things with you. We do the hard work so you don’t have to.</p><p><em>First, a word about Daylight Saving Time</em></p><p>Most of the world has a sensible approach to time. Here in the USA, we suffer from a long-failed experiment to win back more daylight. This manifests in confusing three-character time zone codes (PST, PDT), encoding not only where you are on the planet, but also any silly local offset to which you might be subjected. Unsuspecting users may simply overlook the bizarre behavior of their mobile app when, twice a year, we abruptly redefine “now” as “an hour ago” or “an hour from what was otherwise now.” This nuance of US federal law requires everyone to introduce a different offset suddenly on pre-coordinated dates. We all “spring forward” or “fall back”, leaving our computers wondering what is going on.</p><p><em>Sure, but it’s still just a time zone offset, right?</em></p><p>So far, we’ve gotten lucky. It’s currently cost-prohibitive to travel faster along the surface of the planet than one time zone per hour. For the collective sanity of all my fellow engineers, let’s hope this continues long enough for compound interest in our pensions to distract us. Until then, we’re comfortable knowing there is very little chance of a user’s going for a run in San Francisco then jumping on a plane before their wearable has had a chance to sync, only to sync in New York, making it look like they ran three hours ago. Worse still, if there’s any sort of bug related to time, it quickly becomes unclear if values in the future are actually in the past.</p><h3>Pitfall #0: Assuming Local Time / Ignoring Offset</h3><h4>Problem</h4><p>You board a transcontinental flight in San Francisco at 12pm, flying east. When you land in New York, the local time is probably around 9pm. Without crystal clear agreement of the time zone offset, your phone syncs with your connected device, and data is recorded based on the time stamp from the originating time zone. Then, when your phone has a chance to sync with the cell tower network, it adjusts the clock accordingly. This wreaks havoc on your app’s ability to align itself in space-time.</p><p>If your mobile device is logging current time without clear and distinct policy surrounding time zone offset, all bets are off. Put another way, it’s critically important all the components of your ecosystem agree about the current time. This is magnified when you have users spanning multiple time zones.</p><h4>Symptoms / Early Warnings</h4><ul><li>Odd time shifts — Data recorded apparently from the future, but contextually pinned to the past. This means your ecosystem is potentially out of sync with itself. If you find yourself filtering out future timestamps in an SQL query, you may suffer this problem.</li><li>Unique index constraint violations — If you use a database system where the time stamp is used as a primary key, as is common with time-series data, you may experience bizarre constraint violations when a device changes its system clock. Some records may have conflicting timestamps due to overlaps introduced by the clock change. Records received before the clock change may overlap with those received afterward.</li></ul><h4>Solution: Standardize on UTC (Zulu) time</h4><p>If every device participating in your ecosystem exists on GMT, the only time offsets your systems will experience are relativistic in nature.</p><h4>Reasoning</h4><p>Let’s be honest — time zone confusion is a 20th century problem. If we’re looking forward to 21st century problems, relativistic time distortion seems par for the course.</p><h3>Pitfall #1: Assumptions About Reference Times</h3><h4>Problem</h4><p>As a general rule, software engineers love functions with inputs and outputs. It’s easy to think of things in these terms. We can provide a controlled set of input values and measure the output, comparing it against expectations. If the values match, the test passes. You could spend your whole career, building a rich and complex understanding of the systems you curate, and never encounter the scenario I’m about to share. Hopefully, this helps you to avoid what I suffered.</p><p>I once designed and built a dynamic rules-based event processor system. This system reacted to simple events with complex reactions, based on conditions defined by a downloaded file. One of the components of this system was a time filter. If an event occurred within some threshold of a reference time, the function would return true. Naturally, I created a test for this. Tests passed, and code review was positive, so I merged the changes. A few days later, one of the tests failed.</p><p>The triage team scratched their heads and thought “well, if it happens again, let’s re-evaluate.” The next day, the tests passed, so the triage team disregarded the issue as an isolated anomaly. A week later, the same test failed again. This time, the triage team escalated the issue directly to me, saying “we don’t know why, but it happened again.”</p><p>At this point, I had the benefit of perspective. When I saw the report, I thought there are very few reasons for this sort of problem. It could be concurrency failure. We had previously had concurrency issues with some C++ standard libraries. (There’s a special place in hell for std::localtime.) So, I checked for use of undesirable libraries, and I found we were doing everything right. Then, I saw the culprit. Deep inside a logic flow, I found this:</p><blockquote><em>NSDate* now = [NSDate date];</em></blockquote><p>On its face, this is innocuous. Surely, it’s normal to know when things happen. Yes. Normally, yes. However, in the world of unit tests, where we have prescriptive expectations, the consideration of “now” is weird. From the perspective of the test, I had written something like this:</p><blockquote><em>assert(!isTuesday, “encountered Tuesday”);</em></blockquote><p>Of course no one would write this, and I certainly didn’t write this intentionally. It ended up looking more like this:</p><blockquote><em>assert(expectedDayOfWeek == actualDayOfWeek, “unexpected day of week”);</em></blockquote><p>I’m sure we can all agree we the nuance is easily lost.</p><h4>Symptoms / Early Warnings</h4><ul><li>Repeating failures on specific days of the week.</li><li>Assumptions about “now” — Any time you see some code attempting to measure the current time, take extra care in assessing the risks. This goes double for delayed invocation, dispatching blocks to asynchronous queues. It’s easy to inadvertently introduce an unexpected delay.</li></ul><h4>Solution: Injecting Time Values (Don’t assume “now”)</h4><p>The simple solution is to move the “now” evaluation up one level to the calling component. In this case, it’s a simple matter of refactoring, like this:</p><blockquote><em>// TimeCondition.swift<br>// determines if delta between date and reference is greater than<br>// a specified threshold</em></blockquote><blockquote><em>// old<br>func isSatisfied() -&gt; Bool {<br> let date = Date() // ← assumption about “now”<br> return date.timeInterval(since: self.referenceDate) &gt; self.thresholdValue<br>}</em></blockquote><blockquote><em>// new<br>func isSatisfied(date: Date) -&gt; Bool {<br> return date.timeInterval(since: self.referenceDate) &gt; self.thresholdValue<br>}</em></blockquote><h4>Reasoning</h4><ul><li>Inversion of Control — Requiring an input to the method enables a wider variety of potential uses. This also allows you to craft your tests with specific values for known edge cases, rather than relying on hidden assumptions.</li><li>Self-describing Design — When a developer encounters your method descriptor with a date value in its signature, they know immediately they will be required to provide a date.</li></ul><h3>Pitfall #2: Device System Clock</h3><h4>Problem</h4><p>Mobile phones offer many luxuries to users. One such convenience is the ability to synchronize the system clock to the universally adopted definition of “now”. The vast majority of users have enabled this useful feature. However, there are some users who have adjusted their device system clocks, and their device does not agree with the internet about what time it is. This manifests in strange data, either from the future or the past.</p><p>When working with a distributed hardware ecosystem, where some computation happens on a Motiv Ring and some happens on a mobile device, time synchronization is even more critical to overall system health. When we’re measuring time-series data, it’s essential to have nearly perfect agreement between devices about the current time.</p><p>Everything in the universe experiences time slightly differently, due to motion relative to other things in the universe. For example, clocks at substantially different altitudes experience subtle drift in their values, when measured over long periods of time. To account for this, the clock in a Motiv Ring is synchronized with the mobile device each time we sync. If these values diverge, it can manifest in very unusual ways.</p><h4>Symptoms / Early Warnings</h4><ul><li>Negative or Large Time Offsets — We use a cloud-based analytics platform to receive application log data from devices in the wild. Our system includes consideration for both the time stamp when the event occurred and when the log was received by the web service. Large discrepancies between actual and received time stamps can indicate non-standard system clock.</li><li>Unique index constraint violations — As noted above, using the system time as a primary key for records created at a specific time can lead to unique constraint violations.</li></ul><h4>Solution: Use Network Time Protocol (NTP)</h4><p>Rather than relying on the user to specify the correct time, query an NTP server cluster on app launch. This way, you avoid any unexpected behavior due to user customization of their device.</p><blockquote><strong>Note</strong>: this only works for network-connected devices.</blockquote><p>Lyft offers a convenient Swift library for integrating this functionality into your iOS app. (<a href="https://eng.lyft.com/freezing-time-6ebe8ffe3321">https://eng.lyft.com/freezing-time-6ebe8ffe3321</a>)</p><p>Instacart also developed a similar library, called True Time. (<a href="https://tech.instacart.com/offline-first-introducing-truetime-for-swift-and-android-15e5d968df96">https://tech.instacart.com/offline-first-introducing-truetime-for-swift-and-android-15e5d968df96</a>)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1dda984187a0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/effective-unit-tests-for-time-travel-1dda984187a0">Effective Unit Tests for Time Travel</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Engineering Principles]]></title>
            <link>https://medium.com/motiv-engineering-blog/engineering-principles-1c3580cbcbff?source=rss----427c0b6ef0fd---4</link>
            <guid isPermaLink="false">https://medium.com/p/1c3580cbcbff</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[engineering-culture]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[engineering-mangement]]></category>
            <dc:creator><![CDATA[Motiv Engineering]]></dc:creator>
            <pubDate>Fri, 09 Mar 2018 07:05:07 GMT</pubDate>
            <atom:updated>2018-03-09T07:06:03.743Z</atom:updated>
            <content:encoded><![CDATA[<p>Welcome to the <a href="https://mymotiv.com/">Motiv</a> Engineering blog! In this series, we hope to share more about our team, how we work, and some fun and informative stories about lessons learned on our journey.</p><p>To start with, we wanted to share a little about how we think about teams and how we work together.</p><p><strong>A few bold claims</strong></p><p>The best teams share a common purpose: tactical execution towards a strategy to complete a mission to fulfill a vision. They work together to achieve this vision by excelling in their individual roles and collaborating effectively towards those shared goals.</p><p>In complex, uncertain, ambiguous environments (hat tip to Andy Grove¹), it’s only possible to be effective through shared culture — by that, I mean what kind of behavior is tolerated and encouraged.</p><p>One great way to build culture is intentional: by writing down what you want and having every team member embody and reinforce the team’s principles and standards. This helps create psychological safety² and cultural predictability.</p><p>What are some tools we use to encourage effective and positive human behavior? Let’s look at principles, standards, and autonomy.</p><p><strong>Principles</strong></p><p>Principles should be inviolate. Every action we take should be in accordance with our principles. Therefore, we should have a few, carefully chosen, memorable principles that are in natural harmony.</p><p>Motiv Engineering has chosen the following four principles, in no particular order:</p><ul><li>User-centric: Everything we do is centered around the users of products.</li><li>Pragmatic: We balance the short term and the long term, acting in the best interest of our customers, teammates, and the company.</li><li>Holistic: We embrace big-picture thinking, taking responsibility for the outcome, and owning our work end-to-end.</li><li>Respectful: We treat our teammates with respect and empathy — healthy conflict is important to any functioning team; handling it well is critical.</li></ul><p><strong>So what?</strong></p><p>How did we come to these principles? What do they mean, practically? Stay tuned for another post in this series and even more about standards and autonomy.</p><p>What principles and standards have made your teams effective and happy? What anti-patterns have made your teams ineffective or unhappy? Share your experiences.</p><p>Lastly, we’re hiring in a variety of engineering roles across the company. Apply <a href="https://mymotiv.com/careers">at our careers site</a>!</p><p><strong>Citations</strong></p><p><em>High Output Management</em>, Andy Grove</p><p><a href="https://www.nytimes.com/2016/02/28/magazine/what-google-learned-from-its-quest-to-build-the-perfect-team.html">What Google learned from its quest to build the perfect team</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1c3580cbcbff" width="1" height="1" alt=""><hr><p><a href="https://medium.com/motiv-engineering-blog/engineering-principles-1c3580cbcbff">Engineering Principles</a> was originally published in <a href="https://medium.com/motiv-engineering-blog">Motiv Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>