Recently I took a vacation to an island in the gulf. I have a pretty good cell network, so I could still sometimes get service (but not always). I also could sometimes get Wifi (but not always).
And it became immediately evident which of the apps on my phone were built by teams of 40 and 400 developers.
During regular life, I can tell the difference between an app backed by 4 people and an app backed by 40. But the 40 and 400 distinctions can be more subtle, which is why this post is coming right after a unique circumstance that made it easier to tell which was which.
I’d like to talk about these differences because they’re not the difference that tech-adjacent folks tend to think they are.
For brand new apps, the business tends to think it goes like this:
- Hire 1 or 2 people and build a bare-bones thing.
- Hire 2 more people and build some more features.
- Hire 5 more people and build even more features.
- Hire 30 more people, and we’re no longer resource constrained to write new features.
- We’ll never need 400 developers for one app.
But tech and business interpret the term ‘feature’ differently.
A feature is an intentional change in the app that is noticeable to the user.
To the business, this can sound like it means ‘a button, page, or some new additional functionality.’
But this isn’t always the case.
You can get more features with more developers. However, functionality is one specific kind of feature, and it’s only one of the kinds of features your app will need as it grows.
When you get more developers, your app has grown to the point that your team must devote time to other kinds of features (as well as bugs and chores, which we’ll leave aside for now). The other kinds of features don’t look like buttons or pages or fancy predictions.
How do I know which apps on my phone have 4, 40, or 400 developers? Not by which ones let me do the most stuff, or have the fanciest UI, or have the coolest machine learning behind them.
I know it based on which apps still work in non-ideal situations.
For example, mobile apps usually fetch data by using a cellular connection, by using wifi, or both. Which of my mobile apps work on a really poor connection? Which ones only stop working when I’m connected to neither my cell network or Wifi?
Apps can also be built with a local-first strategy so that they function as much as possible even if they have no data connection. Again, a mobile app example: I can tell which of my two music apps has a larger development team based on which one does nothing without an internet connection and which one can play my three favorite songs, no problem. The second team has the bandwidth to cache a few songs. (When and how to cache and uncache have attracted general regard among programmers as hard problems. So a small tech team will typically defer them as long as they can).
Another example, this time a desktop app: Chrome, Google’s web browser. If you have no internet connection, Chrome shows you a page with a dinosaur on it. If you tap your space bar, that dinosaur will start running, and you can tap the spacebar to jump the dinosaur over cacti. It’s a tiny video game that you can play. It’s not terribly useful, but it shows that the Google team had the bandwidth to devote thought to what Chrome could do without internet—even though the whole point of Chrome is to use it to access the internet.
Functionality is one kind of feature, and it gets prioritized on teams of 4. But there are other kinds of features.
For example, we have these kinds of features:
- Scaling: The app works over slow connections. If there is more traffic than the connection can handle, the app has a fallback. The app was designed local-first, so functionality that does not require a remote connection works even before a remote connection is made or without a remote connection. The app can handle write conflicts to the database if multiple users make incompatible changes at the same time.
- Robustness: The app has functionality, and that functionality works. There are no edge cases where the page cannot be found because some id doesn’t get passed through. There are no forms whose entries can pass validation and also cause the app to crash. There are no request timeouts on a normal, speedy internet connection. There are no random and confusing failures. There are no weird quirks where you have to hit the button twice, or click just to the left of the button, to get the form to submit.
- Security: The app insists on authentication for seeing, touching, and modifying user-specific information. The app encrypts all sensitive data over a connection using a field-tested encryption standard. The app does not ever log or store authentication credentials in plaintext. The app is protected against brute-force attacks, SQL injection attacks, and distributed denial of service attacks, to name a few. There is admin-level authentication for accessing aggregate data, and anyone with those credentials is trained against phishing attacks and social engineering attacks.
- Accessibility: The app is 508 compliant. It has large tap targets that work for folks with poor fine motor function. The text is in a large enough font for nearsighted folks to read, and it is annotated for compatibility with the accessibility options of phones that allow users to increase the font size on all their apps. The colors are high-contrast so folks with colorblindness or sight impairments can tell what’s happening on the screen. The screens are compatible with screen readers through aria annotation. There are no flashing gifs or other epilepsy triggers.
- Inclusion: The app’s design keeps in mind the experiences of folks from various ethnic, socioeconomic, gender, and orientation groups. It does not ask about sex and gender if it does not need this data (legal requirements for health insurance are an adequate reason. Ad targeting is not an adequate reason). It does not use ethnicity information to determine who should get ads for a service. It also does not use zip code delineations inside a metropolitan area to determine who should get ads for a service (this is tantamount to using ethnicity, due to segregation). If there’s a social interaction component, there are features in place for blocking and banning harmful users.
A team of 4 developers has time for functionality features.
A team this small is focused chiefly, if not exclusively, on giving the app the functionality that it needs to be viable in the sunny-day case. It can do most or all of the things that the business needs it to do…sometimes. But it craps out in a non-negligible number of cases. There are enough bugs that users are aware of them and have developed methods of their own to work around them. Sometimes the app goes down.
A team of 40 developers can put more focus on scaling, and sometimes robustness.
When you go from 4 to 40 developers, the functionality of your app is not going to look radically different than it did when the development team was 4. However, that functionality will work for more users. The app will be able to handle the workload associated with all the customers and users that the business wants to add. The business and the documentation can teach users to work around bugs, but it cannot teach users to use an app that won’t work with the number of users that it has. This is, I think, why scaling gets prioritized over robustness on a lot of teams, even though at first glance you’d think it would be the other way.
A team of 400 developers can put more focus on robustness, and sometimes security.
When you go from 40 to 400 developers, the functionality of your app will look different than it did when the development team was 40, but not by as much as you imagine. However, that functionality will work more often. There will be fewer crashes, timeouts, and weird sequence-of-events bugs. Even the bugs that users know how to get around will get fixed, for the most part. Your team will also turn to what can go wrong, not just when the technical winds aren’t in your favor, but when someone actively tries to sabotage you. They’ll put more measures in place to make sure the data is safe.
Even the largest tech teams struggle with security, accessibility, and inclusion.
A lot of the big cases that we see in the news about breaches and big inclusion blunders come from huge companies. Even with an enormous tech team, it’s tough to adequately cover all these kinds of features because resources are limited and also because these types of features don’t tend to get prioritized. Unless the top brass have personally experienced a security breach, they’re not necessarily thinking about what could go wrong on that front. Unless the top brass have personally experienced accessibility issues, they’re not thinking about what that’s like for folks who have. Unless the top brass come from marginalized groups—and usually, in majority, they don’t—they miss the impact of features like these on their product.
The top brass also aren’t load-testing the app. They’re not checking the app over a crappy connection on an airport runway unless the app is literally intended for use on an airport runway (I’ve written on one like this, and we still screwed up and designed it web-first the first time). They’re opening it up in their office on their fast internet connection and viewing the results for their individual requests.
So the features besides functionality get a lot less visibility at decision-making levels.
As a result, the tech team gets incentivized toward focusing on functionality. The other types of features get bandwidth after the tech team has more bandwidth—but once that bandwidth is allocated, a smaller proportion of the team is building functionality, and so development can seem to the business to go slower relative to the size of the tech team.
This is not necessarily bad or wrong. But I hope that this is heartening news for you if you’ve ever grown a development team and then felt disappointed about the amount of progress you’re seeing.
Because when we define ‘feature,’ we say ‘a difference that is noticeable to the user.’ But that begs the question: noticeable to which user? Maybe not always noticeable to the product manager connecting over ethernet. But certainly noticeable to the user base as it balloons. And noticeable to new users who find your onboarding experience less painful than your competitor’s. And noticeable to the marginalized and/or disabled segment of the market that your competitor forgot about. And noticeable to everyone that switches over to your service because they just found out that your competitor acts cavalierly with their personal data.
The features besides functionality don’t matter…right up until they do.
So when your team grows and seems to slow down, you can take comfort in the knowledge that they’re hardening the app to work outside of the sunny-day case.
And who knows? Maybe it will be worthwhile to prioritize more cases like this even before the team gets bigger.
The big differences in apps built by small development teams and large development teams don’t look like a vast difference in flashy functionality. Instead, larger teams have the bandwidth to work on other kinds of features—especially scaling and robustness and also, if your product team prioritizes them, security, accessibility, and inclusion. So between a small team and a large team, the app might work largely the same in the sunny-day case. But the more your app grows, the more often users will face non-sunny-day cases, and these other types of features harden your app for that.
So if you hire a bunch of new tech people and the app on your computer doesn’t suddenly evolve into leviathan version of its former self, take heart. A larger and more varied group of users is noticing the difference, and they appreciate you for it.