<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Scott Berrevoets - Engineering Culture</title><link href="https://scottberrevoets.com/" rel="alternate"/><link href="https://scottberrevoets.com/feeds/engineering-culture.atom.xml" rel="self"/><id>https://scottberrevoets.com/</id><updated>2026-10-05T00:00:00-07:00</updated><entry><title>Why big companies are bad platform citizens</title><link href="https://scottberrevoets.com/2026/10/05/why-big-companies-are-bad-platform-citizens/" rel="alternate"/><published>2026-10-05T00:00:00-07:00</published><updated>2026-10-05T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-10-05:/2026/10/05/why-big-companies-are-bad-platform-citizens/</id><summary type="html">&lt;p&gt;In the fall, when Apple releases new iPhones and major iOS versions, small/indie
developers take a lot of pride in being ready for this new tech on day 1. They
spend all summer learning new APIs and development tools, and integrate them in
their apps so their users can …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the fall, when Apple releases new iPhones and major iOS versions, small/indie
developers take a lot of pride in being ready for this new tech on day 1. They
spend all summer learning new APIs and development tools, and integrate them in
their apps so their users can enjoy them as soon as the platforms they run on
become available.&lt;/p&gt;
&lt;p&gt;This excitement about building for updated platforms couldn't be more different
at big tech companies: LinkedIn got dark mode support two years after Apple
introduced it, YouTube required a premium subscription for PiP (an OS level
feature), and many apps are still due for a Liquid Glass update. It seems
counterintuitive: the engineering teams behind these apps are very skilled,
large, and have demonstrated they can ship fast. So why are they so slow with
these platform-level features and integrations?&lt;/p&gt;
&lt;p&gt;It's a fundamental difference in priorities: small app developers care about
their craft and are proud of the work they're doing. They enjoy using the latest
OS features, integrations, and technologies and making them (more) available to
their users, even if the road to get there is rocky. They hope to get featured,
get special recognition from Apple, and be part of keynotes or other marketing
materials. The blogs, pundits, and tech influencers will highlight their work
regularly enabling another good marketing opportunity.&lt;/p&gt;
&lt;p&gt;But they're usually also the first to admit those moments don't tend to lead to
big spikes in downloads or sales. Even if there is a big spike, interest tends
to wane and after the news cycle for the new tech ends, so does the increased
interest in the indie apps.&lt;/p&gt;
&lt;h3 id="excitement-wanes-fast"&gt;Excitement wanes fast&lt;/h3&gt;
&lt;p&gt;In some cases big app companies immediately jump on this new tech as well,
especially if they can be a launch partner on new tech that Apple or Google
market heavily. We saw this at Lyft too: when Apple first introduced 3rd party
Siri integrations and a more capable watchOS SDK we spent a good amount of time
in the lead up to WWDC partnering with them. They highlighted that work during
the keynote and we felt great about being good iOS citizens. We then spent a
good amount of the summer improving the experiences, polishing the UI, and
ensuring it was bug-free, so that we could launch all this on day 1.&lt;/p&gt;
&lt;p&gt;But months later, usage was more or less 0 for both the Siri integration and
Apple Watch app. During a hackathon we also developed an Amazon Alexa app that
basically no one except for a few very excited Alexa users ever touched. These
integrations are fun to build, but don't tend to do much for user acquisition or
engagement. Rarely do these platform-level integrations make the experience of
an app &lt;em&gt;so&lt;/em&gt; much better that users choose you over a competitor. If they can't
tell Siri or Alexa to get you a ride, they'll just open the app instead.&lt;/p&gt;
&lt;p&gt;Eventually, we dropped the Apple Watch App, Siri and Apple Maps integration, and
the Alexa app became unmaintained. Siri Shortcuts and app clips were not likely
to drive many users so were never implemented. Widgets were built in a hackathon
and were simple enough to support but also didn't see a ton of usage.&lt;/p&gt;
&lt;p&gt;This is true for most companies whose app connects to their larger service:
banks, airlines, ride-sharing, social media, etc. Users are there to get
something done using that particular service and if Apple Pay isn't available or
they can't quick-launch the app through a Live Activity or a widget, they'll
find another way.&lt;/p&gt;
&lt;h3 id="theres-always-a-cost"&gt;There's always a cost&lt;/h3&gt;
&lt;p&gt;Meanwhile the cost of building platform integrations is easy to underestimate.
Codebases tend to run into multiple millions of lines of code with highly
complex architectures, many layers of tech debt, and a varying amount of red
tape to promote accountability and good use of engineering resources. Teams have
seen attrition, understaffing, and reorgs which all hurt the understandability
of the codebase and developer productivity. Early adopters also deal with all
the bugs, so being first means working through those pains as well.&lt;/p&gt;
&lt;p&gt;Large companies also tend to go through quarterly or bi-annual planning cycles,
so what they work on is defined well before the platform vendor rolls out its
updates. Before deciding to prioritize a new platform integration, teams will
want to measure how that project contributes to company goals and make sure it's
not hurting any of the business metrics it cares about.&lt;/p&gt;
&lt;h3 id="being-a-good-citizen-can-be-bad-for-business"&gt;Being a good citizen can be bad for business&lt;/h3&gt;
&lt;p&gt;That's assuming the metrics will improve (or at least stay neutral) by being a
good platform citizen, but in some cases user engagement, ad impressions, or
in-app upsells are actually expected to be negatively impacted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Letting users take action in your app/service without having to open it (e.g.
  through Siri, Shortcuts, or other system integrations) often means you lose
  the opportunity to upsell or show ads. We didn't love how Apple Maps showed
  the price of Uber and Lyft rides side by side without us even knowing how we
  compared on that specific ride. We wanted users to open the app so we could
  win them over if needed.&lt;/li&gt;
&lt;li&gt;Marketing push notifications are despised by people in tech (me included!),
  but the reality is they work &lt;em&gt;really&lt;/em&gt; well as an engagement or reactivation
  method for most people. Personally I revoke push notification permission if
  apps do this, but that's simply not true for the majority of people that use
  these services.&lt;/li&gt;
&lt;li&gt;Adtech is a multi-billion dollar industry, so despite App Tracking
  Transparency and Apple's efforts to not let app developers spy on their users,
  companies try to use any workaround they can think of to improve their
  attribution, conversion, and ad relevancy. "Do as much on device as possible
  and request as little user data as possible" is just not something companies
  are interested in: they &lt;em&gt;want&lt;/em&gt; the data. WhatsApp could easily work without
  access to your contacts, yet to start a new chat it requires that access
  anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In some cases, the company's incentives align with the platform, which is a win
for everybody but generally still a decision driven by the company's own goals.
The recent wave of companies choosing native over React Native is for
engineering reasons; the fact that those apps will now offer a native user
experience is just a nice side benefit.&lt;/p&gt;
&lt;p&gt;All of this is in stark contrast to the smaller developers that first and
foremost care about the craft and user experience. The quality and "at home"
feeling of their app matters more as they often rely on the platform's biggest
enthusiasts to use and review their apps. At larger organizations, this type of
craft is often driven by individual engineers as passion projects and is usually
secondary to business value.&lt;/p&gt;</content><category term="Engineering Culture"/><category term="Technology Adoption"/><category term="Engineering Culture"/><category term="Technical Debt"/></entry><entry><title>Review your own AI-generated code</title><link href="https://scottberrevoets.com/2026/03/20/review-your-own-ai-generated-code/" rel="alternate"/><published>2026-03-20T00:00:00-07:00</published><updated>2026-03-20T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-03-20:/2026/03/20/review-your-own-ai-generated-code/</id><summary type="html">&lt;p&gt;Years ago, when we started to have 5+ contributors to our codebase at Lyft, we
had to establish a code review process with high enough value to justify the
friction it caused. However, "value" is very subjective, as not everyone cares
about the same qualities of a pull request:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Catching …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Years ago, when we started to have 5+ contributors to our codebase at Lyft, we
had to establish a code review process with high enough value to justify the
friction it caused. However, "value" is very subjective, as not everyone cares
about the same qualities of a pull request:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Catching runtime problems like bugs, security vulnerabilities, or performance
  issues&lt;/li&gt;
&lt;li&gt;Knowledge and context sharing, mentoring, or general collaboration&lt;/li&gt;
&lt;li&gt;Code quality and maintainability&lt;/li&gt;
&lt;li&gt;Upholding code consistency, conventions, style, naming, grammar, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over the last 10-15 years, the industry has seen its &lt;strong&gt;engineering foundations&lt;/strong&gt;
greatly improved: programming languages, CI systems, build tools, internal
architectures and processes, and engineering principles have improved so much
that human review of many of these potential issues has lost a lot of their
value. Linters keep code consistent as much as possible, standardized
architectures should enable reliability and testability, modern programming
languages and paradigms enable low code complexity, and CI validates all these
conditions before deploying.&lt;/p&gt;
&lt;p&gt;With these advancements over the years, I find myself leaving significantly
fewer comments on PRs than before. Any comments I do leave are usually about
local, low-severity code quality improvements, avoiding suppressing linter
violations, asking for specs/context, double-checking intention, etc. Whenever
possible, I try to instead invest in some of these foundations: an additional
linter rule, updating CI, providing better architectural patterns. That time
investment scales much better than me leaving comments on individual PRs.&lt;/p&gt;
&lt;p&gt;And that brings us to 2026, where we get to re-evaluate the "value" of code
review because of AI agents. The impact of AI on code review is threefold:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Output quality&lt;/strong&gt;: a year ago we called it vibe-coding, today it's much more
   difficult to tell apart agent code from human code if the human(s) behind the
   agent code put real effort into getting high-quality output from the agent&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Agentic review&lt;/strong&gt;: many teams have agents review other agents' code. Codex
   has &lt;code&gt;/review&lt;/code&gt;, Claude Code has &lt;a href="https://code.claude.com/docs/en/code-review"&gt;Code Review&lt;/a&gt;, and Cursor, Copilot, and
   various observability and SRE tools all have code review agents as well.&lt;/p&gt;
&lt;p&gt;I've made code review part of my own workflow as well: write a plan, have
   an agent refactor a bunch of code, and have other agents review it for
   issues. &lt;a href="https://openai.com/index/harness-engineering/"&gt;OpenAI&lt;/a&gt; is, unsurprisingly, pushing the boundaries with an
   internal project where all code is reviewed by agents and mostly not by
   humans at all.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code volume&lt;/strong&gt;: engineers are "writing" a lot more code than before and, if
   we stick to our old practices, the burden of code review increases more too&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We have gotten faster at producing high-quality code, but if it can't be
reviewed at higher speed as well, we've just found ourselves a &lt;em&gt;new&lt;/em&gt; bottleneck.&lt;/p&gt;
&lt;p&gt;The status quo is that engineers review &lt;em&gt;each other&lt;/em&gt;'s code to avoid &lt;strong&gt;author
bias&lt;/strong&gt;, which could lead to overestimating the quality, performance, and clarity
of the code written by the author. But in a world where the entire development
workflow is changing and code is increasingly written by agents, we're no longer
actually reviewing &lt;em&gt;each other&lt;/em&gt;'s code, we're reviewing &lt;em&gt;agents'&lt;/em&gt; code. The bias
no longer exists at the code level; it moves to the plan level.&lt;/p&gt;
&lt;p&gt;Human accountability and oversight are still important, but &lt;strong&gt;the primary
responsibility of code review can shift to the engineer that drove the agent in
the first place&lt;/strong&gt;. That engineer is coming in with all the context and can
review the code much faster than other engineers. For bigger changes, their
author bias could still apply to the &lt;em&gt;plan&lt;/em&gt;, so it's good to have other
engineers review that, but the code itself is just execution.&lt;/p&gt;
&lt;p&gt;With the right plan and LLM guidance in place, agents have gotten excellent at
implementing exactly according to spec. When an agent introduces a bug, it's
less likely to be an implementation issue and more likely an underspecification
of the plan, guidance, or documentation. The correctness of these documents is
still a shared team responsibility.&lt;/p&gt;
&lt;p&gt;The outcome is more than just removing the new bottleneck in writing code.
Engineers are more autonomous in shipping because they don't have to wait for
others, possibly in different time zones, and can fully unblock themselves in
all the work they are doing. With strong PR checks, such as linters and code
quality gates, in place to prevent old-fashioned vibe-coding, the bar remains
high while increasing velocity.&lt;/p&gt;
&lt;p&gt;There is one notable downside to this approach: the reduced context sharing and
mentoring. It's a real benefit of "traditional" code review, but not the most
effective time to break knowledge silos. As I mentioned earlier, &lt;em&gt;plans&lt;/em&gt; still
warrant review by others, and plans are usually derivatives of tech specs or
PRDs. A well-written document provides the necessary context, the whys, the
thought process, and the alternatives considered. It's also easier to ask
questions and act on feedback in this stage because it's much earlier in the
development lifecycle.&lt;/p&gt;
&lt;p&gt;In a world where agentic engineering is the norm, teams review plans, specs, and
agent guidance together so they capture the intended design and behavior; the
code becomes an implementation detail of the plan that can be self-reviewed by
the driver of the agent.&lt;/p&gt;</content><category term="Engineering Culture"/><category term="Developer Experience"/><category term="Engineering Culture"/></entry><entry><title>All in on Interface Builder: 10 years later</title><link href="https://scottberrevoets.com/2026/03/02/all-in-on-interface-builder-10-years-later/" rel="alternate"/><published>2026-03-02T00:00:00-08:00</published><updated>2026-03-02T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-03-02:/2026/03/02/all-in-on-interface-builder-10-years-later/</id><summary type="html">&lt;p&gt;Back in 2017, I wrote about how we used &lt;a href="https://scottberrevoets.com/2017/03/06/using-interface-builder-at-lyft/"&gt;Interface Builder&lt;/a&gt; as our primary way
of building UI at Lyft. Interestingly, Speak chose to use Interface Builder to
build UI too before SwiftUI came along, though without the same tooling we had
at Lyft. With Interface Builder mostly forgotten, including …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Back in 2017, I wrote about how we used &lt;a href="https://scottberrevoets.com/2017/03/06/using-interface-builder-at-lyft/"&gt;Interface Builder&lt;/a&gt; as our primary way
of building UI at Lyft. Interestingly, Speak chose to use Interface Builder to
build UI too before SwiftUI came along, though without the same tooling we had
at Lyft. With Interface Builder mostly forgotten, including by Apple itself, I
think it's worth sharing some lessons learned after all this time.&lt;/p&gt;
&lt;p&gt;I didn't explicitly share this in the original post, but we didn't take the
decision to use Interface Builder lightly. The main benefits we saw:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Swift toolchain was very young and not fully developed yet. The compiler
  was slow and autocomplete was much less advanced, so the more code we could
  shove in Interface Builder the less code that had to be compiled. This had a
  significant impact on build times and build failures for us.&lt;/li&gt;
&lt;li&gt;We were hoping for what Swift Previews eventually got much closer to: a quick
  way to understand what a view looks like at runtime without spinning up a
  simulator&lt;/li&gt;
&lt;li&gt;It saved developers a lot of typing manual, tedious Auto Layout code.
  Interface Builder provide immediate feedback about whether your constraints
  resolved cleanly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We were also aware of the pain points of Interface Builder and built good
tooling around managing those. We got to meet with Apple engineers to voice IB's
weaknesses that we couldn't tool our way out of, and were generally happy to not
be writing super verbose Auto Layout code. Merge conflicts still never were a
problem because we distributed the UI over many individual storyboards and xibs.&lt;/p&gt;
&lt;p&gt;We felt we'd found a way to make Interface Builder scale pretty well, and it did
for a long time. But cracks started to form in the strategy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We had primarily invested in storyboards, but a stronger focus on code
  testability was directly in conflict with that investment&lt;/li&gt;
&lt;li&gt;New team members never fully got the hang of some of these more nuanced and
  advanced ways of using Interface Builder&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@IBDesignable&lt;/code&gt; never really lived up to its expectations, being buggy, slow,
  and unreliable&lt;/li&gt;
&lt;li&gt;Routing complexity changed our strategy over time to not use segues anymore,
  losing some of the benefits of using IB&lt;/li&gt;
&lt;li&gt;UIs became a lot more complicated and constraint management became difficult
  in Interface Builder (this likely would've also been hard in programmatic UI)&lt;/li&gt;
&lt;li&gt;Building Lyft's internal design system was more difficult while supporting
  Interface Builder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these problems came with scale none of us had really seen before. At the
same time, the original problem of writing programmatic Auto Layout also still
was a concern, so a potential migration was costly with unclear benefits.&lt;/p&gt;
&lt;p&gt;Until SwiftUI came along. With SwiftUI, we finally felt like the cons started to
outweigh the pros. We embarked on what ended up being a five year migration
journey away from Interface Builder, finally completing the effort mid-2024.
This multi-step migration was the largest we had undertaken on iOS (on Android
the Dagger 1 to Dagger 2 migration was of similar scale), and immediately
justified the investment into &lt;a href="https://www.scottberrevoets.com/2021/10/14/ios-architecture-at-lyft/#declarative-ui"&gt;DeclarativeUI&lt;/a&gt; as a prelude to SwiftUI adoption.&lt;/p&gt;
&lt;p&gt;Joining Speak also exposed me to the newcomer perspective of having so much UI
defined in Interface Builder. It's hard to understand the scope, complexity, and
context of a screen when all its configuration is tucked away in the right
sidebar, with many implicit behaviors from checkboxes, dropdowns, and long lists
of constraints and their properties.&lt;/p&gt;
&lt;p&gt;We're also dealing with the same testability problem (even though dependency
injection with storyboards is a little easier nowadays), and the storyboards in
the Speak codebase are more like wireframes than actual designs, so navigating
them isn't any easier than code. Refactoring is harder because the UI definition is
split into two (some in code, some in Interface Builder), and the weakly typed
IB/code connections between &lt;code&gt;@IBOutlet&lt;/code&gt;s and &lt;code&gt;@IBAction&lt;/code&gt;s make it hard and risky
to understand which code is still used and which isn't.&lt;/p&gt;
&lt;p&gt;A more modern problem is that because Interface Builder was not the popular
choice in an ecosystem where open source was already much less valued than in
other development domains, there is very little Interface Builder XML that AI
agents are trained on. This means their effectiveness is also much less compared
to plain Swift, which is doubly annoying when AI agents would otherwise reduce
the migration cost tremendously.&lt;/p&gt;
&lt;p&gt;Let it be clear that I wouldn't recommend Interface Builder as a tool for
building UI anymore, but despite my change of heart, I can't say I regret the
choice of introducing it at the time we did. There were no signs a replacement
UI framework would come along, teams were reasonably productive with it, and we
benefited from its use for a good 4-5 years. Some of the tools and
implementations were arguably too clever for their own good, and that's a
valuable lesson learned.&lt;/p&gt;
&lt;p&gt;But I think the bigger lesson is that there's a cost to architectural decisions
like these, and in an industry that's always changing, the pros and cons need
constant weighing. It's easy to adopt a sunk cost mentality and keep investing
more time and resources because the migration cost is high, but like other tech
debt it just increases the cost of migration at a later stage.&lt;/p&gt;
&lt;p&gt;Relating this to my own post about &lt;a href="https://www.scottberrevoets.com/2022/09/28/human-factors-in-choosing-technologies/"&gt;human factors in choosing technologies&lt;/a&gt;, the
adoption, ramp-up, and support factors were all pretty favorable. However, in
retrospect, IB scored low on the ability to undo (as many architectures would),
and after declarative UIs came along there was a downward trend in whether
engineers &lt;em&gt;liked&lt;/em&gt; using IB. Once that trend started, Interface Builder's days
were numbered, even if the path to obsolescence was long and painful. &lt;/p&gt;</content><category term="Engineering Culture"/><category term="Technology Adoption"/><category term="Technical Debt"/></entry><entry><title>Incremental complexity in iOS development</title><link href="https://scottberrevoets.com/2025/02/03/incremental-complexity-in-ios-development/" rel="alternate"/><published>2025-02-03T00:00:00-08:00</published><updated>2025-02-03T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2025-02-03:/2025/02/03/incremental-complexity-in-ios-development/</id><summary type="html">&lt;p&gt;I wrote my first app, a map to navigate my college campus, in the winter of
2011, on iOS 4.x and with Xcode 4.x. There were 25 frameworks, iOS 7 hadn't been
invented yet, there was only 1 phone size, and we wrote apps with Objective-C.&lt;/p&gt;
&lt;p&gt;But things …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I wrote my first app, a map to navigate my college campus, in the winter of
2011, on iOS 4.x and with Xcode 4.x. There were 25 frameworks, iOS 7 hadn't been
invented yet, there was only 1 phone size, and we wrote apps with Objective-C.&lt;/p&gt;
&lt;p&gt;But things have gotten much more complex over the years: within iOS you can
specialize in build engineering, architecture, performance &amp;amp; observability,
UIKit or SwiftUI, game development, security and privacy, Core Data or
SwiftData, iCloud, cross-platform apps with Catalyst, machine learning... The
list goes on and on, and that's on top of the already powerful frameworks from
the early days.&lt;/p&gt;
&lt;p&gt;There are frameworks that enable entire categories of apps that weren't possible
in the early days, or if they were with much less OS integration than today.
HealthKit, WalletKit, StoreKit, RealityKit, ARKit all enable apps that were
pretty much unimaginable 15 years ago. And that's just iOS without even thinking
about watchOS, visionOS, or Apple's other platforms.&lt;/p&gt;
&lt;p&gt;Swift went through a similar evolution. It started with a more functional
programming language compared to Objective-C, but quickly added new features:
protocol-oriented programming, property wrappers, function builders,
concurrency, macros, and a billion new keywords.&lt;/p&gt;
&lt;p&gt;In my first few years, I was eager to learn everything there was to learn about
iOS. This was in the days when there was still an NDA anything related to iOS
APIs and the developer forums required a paid membership, so only anything
published by Apple was readily available. One of the first things I checked with
new platform versions was the API changes in UIKit and Foundation, and the iOS
and Xcode release notes were next. WWDC keynote days were overwhelming because a
year's worth of changes just shipped and I felt like I needed to learn all of it
in a few hours' time.&lt;/p&gt;
&lt;p&gt;There are so many more sources to learn some part of the platform now that's
impossible to keep up with it all. You can easily be an iOS developer without
knowing 90% of its technologies. A "generalist" iOS developer will know many of
the UIKit/SwiftUI and Foundation APIs, as well as commonly used Swift features.
But if you're building an iOS app for an audio company that requires deep
knowledge of AVFoundation, you won't care or be able to keep up with
advancements in Core Location simply because they're irrelevant.&lt;/p&gt;
&lt;p&gt;This feels like a pretty big difference from the first few years of the App
Store, when experienced developers went "broad" because "deep" didn't really
exist yet. There were no UI experts, iOS game developers, Swift enthusiasts, or
multiplatform lobbyists, so it was fun to learn more about all the frameworks
even if you weren't going to use them.&lt;/p&gt;
&lt;p&gt;For beginning developers that increase in complexity isn't super obvious, as
everyone is "expected" to know basic UI and Swift knowledge and it takes some
time to learn that. But more senior developers looking for a new job where
experience with specific frameworks (sometimes not even Apple's own, like React
Native, RxSwift, or The Composable Architecture) is desired may feel like they
are not experienced enough. In reality they have plenty of great experience,
just in other parts of iOS.&lt;/p&gt;
&lt;p&gt;Most companies that hire for a niche iOS specialty realize this and are
generally OK with someone who's willing to learn the technologies that specific
job requires. But how do those companies interview for that? For hands-on coding
exercises, there are a few options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ask more basic questions&lt;/strong&gt; that all people should know at least to some
  extent. This works OK but doesn't really distinguish senior from junior
  engineers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ask deeper questions&lt;/strong&gt; but run the risk the candidate happens to not have
  deep knowledge on the topic they're being asked about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let candidates choose the topic&lt;/strong&gt; they have deep knowledge of and talk about
  that, but run the risk of the interviewer knowing nothing about topic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having been deeply involved in designing an interview process, I'm growing more
convinced that evaluating candidates based on 60 minutes of hands-on coding
isn't going to get you the best developers and will primarily weed out the below
average ones. What I find more interesting for these positions is if candidates
have enough transferable knowledge in one framework to quickly ramp up with
another. You can learn a ton about someone's experience through questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does Apple approach privacy and how is that reflected in its APIs? What is
  generally involved in requiring user permission to use sensitive user data and
  what is the best way to handle that data?&lt;/li&gt;
&lt;li&gt;What hardware and environmental constraints do iPhones have? How does the
  system deal with those limitations and in what ways are developers affected by
  that? What precautions can you take and how can you know if your app performs
  well?&lt;/li&gt;
&lt;li&gt;How would you do the research for a lightning talk about an iOS technology you
  currently know nothing about (e.g. data persistence)? What sources of
  information are there, how reliable are they, and which do you prefer?&lt;/li&gt;
&lt;li&gt;Think of an API, language feature, or pattern you've used recently that can be
  improved. What did you not like about it, how would you improve it, and why do
  you think Apple might have designed it this way?&lt;/li&gt;
&lt;li&gt;Apple introduces a new technology at WWDC and your boss hears about it and
  wants you to implement it in the company's app. What problems do you foresee?&lt;/li&gt;
&lt;li&gt;What App Store Review guideline would you get rid of and why? Which one would
  you introduce?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These questions feel decidedly more involved than typical questions like "how
does memory management work" and "what's the difference between a class and a
struct", and they give a lot more insight into someone's (lack of) experience
with the platform.&lt;/p&gt;
&lt;p&gt;Being a senior iOS developer went from breadth of knowledge to depth of
knowledge, and I expect more defined specialties, perhaps iOS Security Engineer
or iOS Camera Engineer, to crystallize in the future. It's no longer about
watching every WWDC video and learning the latest UIKit tricks, knowing exactly
what APIs became available in what iOS version, and finding ways to use the
latest Swift features in your own code.&lt;/p&gt;
&lt;p&gt;Instead, more experienced developers understand how Apple designs their
platforms and technologies, have surface-level knowledge of what is available to
them, and are able to take their existing knowledge and apply it to something
new to ramp up quickly and effectively. The rest they know they can learn on the
fly. Hopefully companies realize this too in their interviewing and onboarding
processes, because you might be onboarding an iOS developer who doesn't know a
single thing about the tech stack your app is running on.&lt;/p&gt;
&lt;div class="admonition update"&gt;
&lt;p class="admonition-title"&gt;Update (February 2026)&lt;/p&gt;
&lt;p&gt;This post is only a year old but many shifts have already happened. AI
impacts the profession in a profound way and I no longer think more
specialized jobs will exist. Instead, companies will have their engineers
leverage AI to ramp up on new technologies quickly instead of trying to find
their unicorn engineer that happens to be an expert on Bluetooth audio.&lt;/p&gt;
&lt;p&gt;Senior engineers with a baseline knowledge will need to upskill to learn how
to use AI tools but will be fine in the end. Junior engineers will have a
harder time as companies seem to be unwilling to train them, a bad trend in
my opinion because the junior engineers are the senior engineers of the
future.&lt;/p&gt;
&lt;/div&gt;</content><category term="Engineering Culture"/><category term="Platform Engineering"/><category term="Developer Experience"/><category term="Engineering Culture"/></entry><entry><title>Shifting the testing culture: Motivation</title><link href="https://scottberrevoets.com/2024/11/18/shifting-the-testing-culture-motivation/" rel="alternate"/><published>2024-11-18T00:00:00-08:00</published><updated>2024-11-18T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2024-11-18:/2024/11/18/shifting-the-testing-culture-motivation/</id><summary type="html">&lt;p&gt;Five years ago I began promoting automated testing heavily within Lyft even
though I didn't have any real experience in writing big test suites myself. Many
hands make light work, so I found a few people who shared this thinking and we
started on what turned out a multi-year testing …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Five years ago I began promoting automated testing heavily within Lyft even
though I didn't have any real experience in writing big test suites myself. Many
hands make light work, so I found a few people who shared this thinking and we
started on what turned out a multi-year testing journey. What did we do?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We optimized the architecture in a much more profound way than just "you can
   write tests more easily"&lt;/li&gt;
&lt;li&gt;We built a ton of tooling to improve the developer experience and to hold
   ourselves more accountable&lt;/li&gt;
&lt;li&gt;We started measuring and reporting progress on our efforts regularly&lt;/li&gt;
&lt;li&gt;We included testing in company-wide processes like incident review and
   biannual planning&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The end result of all the major efforts, minor quality-of-life improvements, and
fixing tiny paper cuts has led to test-writing now being a huge part of our
engineering culture. That's not to say we're there—there is definitely still
more work to be done. But the progress is pretty remarkable and I wanted to
share more about it.&lt;/p&gt;
&lt;p&gt;I'll do this in a series of blog posts, this being the first one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Motivation&lt;/strong&gt;: why we thought this was an important area to invest in&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://scottberrevoets.com/2024/11/19/shifting-the-testing-culture-infrastructure/"&gt;Infrastructure&lt;/a&gt;&lt;/strong&gt;: how we upgraded our infrastructure to optimize for
   testability&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://scottberrevoets.com/2024/11/20/shifting-the-testing-culture-code-coverage/"&gt;Code coverage&lt;/a&gt;&lt;/strong&gt;: how we measure, report, and act on our code coverage&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;
&lt;p&gt;After experiencing the hypergrowth of both our team and codebase in 2017-2019,
it became clear that the only way to maintain code and product quality was
through automated testing. Prior to that, a lot of testing would happen through
knowledgeable developers, QA processes, and dogfooding, but that wasn't going to
hold up indefinitely for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;QA time is costly because a human hour is much more expensive than a computer
  hour. Writing a unit test means you can now validate any code change with that
  test in a fraction of a second, but a QA engineer needs to spend that time
  again and again.&lt;/li&gt;
&lt;li&gt;QA processes themselves are expensive: test plan management and maintenance
  costs time and money and the process itself needs to be developed, documented,
  and updated&lt;/li&gt;
&lt;li&gt;People leave, and with them institutional knowledge leaves as well. It takes a
  long time for new people to build that knowledge, if they ever get there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short, computers can handle 80-90% of this work faster and more accurately
than developers can. As with all infrastructure, it takes some time to set it up
in an effective way, but since that's a one-time effort it should pay itself off
over time.&lt;/p&gt;
&lt;h2 id="testing-methodologies"&gt;Testing methodologies&lt;/h2&gt;
&lt;p&gt;Unit testing, acceptance testing, integration testing, system testing,
regression testing, UI testing... fanatics will tell you they're all important.
I don't necessarily disagree, but how hard you go also depends on how you need
to balance quality and velocity. Aviation software leans much more toward
quality, a rideshare app comparably more toward velocity.&lt;/p&gt;
&lt;p&gt;So we initially focused on two types of testing: unit testing and UI testing.
After snapshot testing became popular on both iOS and Android we added that to
our arsenal as well.&lt;/p&gt;
&lt;p&gt;Unit and snapshot tests are written by mobile developers for their own code, UI
tests cover larger flows and are written by SETs. (In my opinion UI tests, if
you choose to invest in them, should also be written by the developers that
wrote the original code, but that is not the setup we have today.)&lt;/p&gt;
&lt;h2 id="benefits"&gt;Benefits&lt;/h2&gt;
&lt;p&gt;It feels a bit weird to be writing these articles pretending it's some novel
idea that writing tests benefits an engineering organization like that isn't
already common knowledge. However, testing culture in the iOS community, and I
suspect all "frontend" disciplines (web, mobile, TVs, etc.), is weak compared to
that in other communities. For one because UI is hard to test, but also because
Apple itself doesn't promote it much as a best practice.&lt;/p&gt;
&lt;p&gt;To not just speak in hypotheticals, the benefits we've experienced firsthand
are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Behavior validation&lt;/strong&gt;: The most obvious benefit is that tests validate how
  code behaves. Bug fixes will see fewer regressions, refactors are safer, and
  the app is generally of higher quality because every bug happens only once (in
  theory) and many are prevented from happening at all.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavior documentation&lt;/strong&gt;: Code becomes more self-documenting as a lot of the
  behavior is prescribed in tests. You can skim test names/descriptions to
  understand how a class is supposed to behave, with little risk of the
  documentation becoming outdated because the tests will fail if they don't
  match the actual behavior.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code readability&lt;/strong&gt;: in order to write reasonable tests, code has to be
  structured reasonably well too. Test setup is hard with spaghetti code, so
  there's an incentive to write cleaner code. (Unfortunately, the opposite is
  true too: poorly written code can often lead to poorly written or no tests.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engineering satisfaction&lt;/strong&gt;: a well-tested codebase is more satisfying to
  work in because developers have higher confidence in the correctness of their
  code. It "feels" easier to make changes because you can rely on the tests to
  catch any mistakes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="drawbacks"&gt;Drawbacks&lt;/h2&gt;
&lt;p&gt;I don't really like writing these articles without also mentioning the
drawbacks. The main ones we've experienced are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time cost&lt;/strong&gt;: it simply takes time to write tests, and there's strong
  correlation between a codebase that &lt;em&gt;desperately needs tests&lt;/em&gt; and the time it
  takes to write those tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;False sense of security&lt;/strong&gt;: it's easy to become overly confident in the test
  suite and assume a passing test suite means there are no bugs, when in reality
  the test suite might be incomplete or of low quality&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning curve&lt;/strong&gt;: there's a learning curve involved, especially in a large
  codebase, and not everyone is interested in learning this&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More code to maintain&lt;/strong&gt;: code requires maintenance and test code is no
  different&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over time we've tried to minimize these drawbacks as best as we could, but most
of them will likely always be a factor. In the next articles I'll go in a
bit more depth on the mitigation strategies we've used and how effective they've
been.&lt;/p&gt;</content><category term="Engineering Culture"/><category term="Testing"/><category term="Engineering Culture"/></entry><entry><title>Human factors in choosing technologies</title><link href="https://scottberrevoets.com/2022/09/28/human-factors-in-choosing-technologies/" rel="alternate"/><published>2022-09-28T00:00:00-07:00</published><updated>2022-09-28T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2022-09-28:/2022/09/28/human-factors-in-choosing-technologies/</id><summary type="html">&lt;p&gt;I recently saw a thread where someone wanted to introduce a more capable
architecture pattern than what most apps start out with in a small team, but
received some pushback from teammates and was looking for help in countering
their arguments.&lt;/p&gt;
&lt;p&gt;The thread for the most part focused on the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently saw a thread where someone wanted to introduce a more capable
architecture pattern than what most apps start out with in a small team, but
received some pushback from teammates and was looking for help in countering
their arguments.&lt;/p&gt;
&lt;p&gt;The thread for the most part focused on the technical benefits of the proposed
pattern, such as testability, separation of concerns, modularity, etc. These are
all valid trade-offs to consider: we’ve done the same at Lyft when figuring out
what architectures would suit us best. This is also not unique to architectures,
any big technology that influences the overall direction of the codebase:
SwiftUI vs. UIKit, RxSwift vs. Combine vs. async/await, etc.&lt;/p&gt;
&lt;p&gt;But over time I’ve realized that even with a technically “perfect” solution (a
real oxymoron!), there is an entirely different yet equally important factor to
consider: who are the people using it—now and in the future? The success of a
particular technology is highly dependent on the answer to that question, and it
plays a huge role at different stages of the development of that technology.&lt;/p&gt;
&lt;p&gt;In the thread I mentioned above there was very little focus on the human aspect
of the proposal, so I wanted to list a number of things that I personally ask
myself and our teams before considering moving forward:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How much is the onboarding/ramp-up cost?&lt;/strong&gt; While I generally think short-term
pain is worth long-term gain, onboarding is often a continuous cost. New people
join, you might have interns, non-mobile engineers wanting to make quick
contributions, etc. If those people first need a lot of time to ramp up, it’s
worth wondering if the benefits are worth it, or how to reduce that burden. For
example, while we currently aren't using any SwiftUI at Lyft, we have a layer of
&lt;a href="http://www.scottberrevoets.com/2021/10/14/ios-architecture-at-lyft/#declarative-ui"&gt;sugar syntax on top of UIKit&lt;/a&gt; that enables us to use SwiftUI-like syntax anyway.
This makes it easier for both new people that join and already know SwiftUI and
for everybody to move over to SwiftUI if/when we're ready for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How easy is it to undo?&lt;/strong&gt; Or: what is the cost of mistakes? If things don’t
pan out the way we want them to, how easily could we switch to something else?
The more difficult switching back is, the higher the commitment level and the
more we need to be sure it’s worth it. This applies both to the internals of the
framework and how the code that uses the framework is structured.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is it easy to do the right thing?&lt;/strong&gt; This one is straightforward: if it’s easy
to do the right thing it’s more likely people will do the right thing and
achieve the architecture’s potential more. Conversely, if it’s easy to do the
wrong thing, the benefits aren’t realized as much. Especially considering my
previous point, if it’s hard to undo bad usage it’s maybe worth going back to
the drawing board.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How much support is available?&lt;/strong&gt; Popular technologies have a lot of online
material available for support in the form of Stack Overflow questions and
answers, blog posts, videos, open source code + discussions on GitHub, etc. A
home-built solution means this knowledge only lives in-house which increases the
bus factor. The same is true for very opinionated third-party libraries like
RxSwift or The Composable Architecture. I’m a fan of both, but without fully
understanding how they’re implemented you’re at the mercy of the developers and
contributors of these libraries for years to come.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How much institutional knowledge does it require?&lt;/strong&gt; Good architectures hide
domain complexity for its consumers, and incur the complexity internally. To
some extent that’s fine, but if the internals become so complicated that few
people know how it works there is again a high bus factor. It can absolutely be
worth putting some complexity/boilerplate burden onto feature owners to avoid
making complex abstractions that are hard to change in the future once it’s used
everywhere and the original developers have left.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How much effort does it take to see 100% adoption?&lt;/strong&gt; Depending on the size of
the existing code base, it could take a long time to get 100% adoption. That can
be OK if this is the codebase’s first serious architecture, but if it’s version
5 and some parts of the codebase still use version 1 through 3, it’s probably
worth removing those first and reducing &lt;a href="http://mikehadlow.blogspot.com/2014/12/the-lava-layer-anti-pattern.html"&gt;lava layers&lt;/a&gt;. Even if the change
from version 10 to 11 is small and easy, the fragmentation of the codebase
inhibits developer productivity. The quicker the migration the better, and if
the codebase can safely be migrated through automation that’s the best case
outcome.&lt;/p&gt;
&lt;p&gt;But the most important one of all: &lt;strong&gt;do people actually like the architecture?&lt;/strong&gt;
No one likes working in a codebase where everything is a hassle, the underlying
concepts never seem to make sense, abstractions are leaky, and you seem to
always have to do work for it instead of it working for you. Those codebases
diminish the team’s motivation levels and will affect many of the other points
from above.&lt;/p&gt;
&lt;p&gt;On the flip side, if people like the proposed patterns, they will put in a lot
more work in to use them correctly, try harder to do the right thing, are
willing to help others, etc. If not, forcing patterns people don’t like could
lead to developer unhappiness and attrition. We have more than a few examples of
this at Lyft, where a slightly inferior technical solution is overall much more
beneficial because the pattern is a bit simpler to use than the alternative.&lt;/p&gt;
&lt;p&gt;Going back to why I started writing this in the first place: in my opinion the
question “what counterarguments can I use” is not a great first question to ask
when it comes to convincing people your solution is the best one out there.
Understanding &lt;em&gt;why&lt;/em&gt; people are resistant is key. Sure, some people just don’t
like change, but papering over any of the concerns above with a technically
superior solution is a recipe for a bunch of barely-adopted technologies in a
codebase that’s often worse off than if nothing had been done in the first
place.&lt;/p&gt;</content><category term="Engineering Culture"/><category term="Technology Adoption"/><category term="Engineering Culture"/></entry></feed>