The “there are only a few screens” is not true. The app works in 60+ countries, with features shipped in the app that often for a country, and - in rare cases - a city.
The app has thousands of scenarios. It speaks to good design that each user thinks the user is there to support their 5 use cases, not showing all the other use cases (that are often regional or just not relevant to the type if user - like business traveler use cases).
Uber builds and experiments with custom features all the time. An experimental screen built for London, UK would be part of the app. Multiply this by the 40-50 product teams building various features and experiments outside the core flows you are talking about (which core flows are slightly different per region as well).
I worked on payments, and this is what screens and components are in the Uber app:
- Credit cards (yes, this is only a a few screens)
- Apple Pay / Google Pay on respective platforms
- PayPal (SDK)
- Venmo (SDK)
- PayTM (15+ screens)
- Special screens for India credit cards and 2FA, EU credit cards and SCA, Brazil combo cards and custom logic
- Cash (several touch points)
- AMEX rewards and other credit card rewards (several screens)
- Uber credits & top-ups (several screens)
- UPI SDK (India)
- We used to have Campus Cards (10 screens), Airtel Money (5), Alipay (a few more), Google Wallet (a few) and I other payment methods I forget about. All with native screens. Still with me? This was just payments. The part where most people assume “oh, it’s just a credit card screen”. Or people in India assume “oh it’s just UPI and PayTM”. Or people in Mexico “oh, it’s just cash”. And so on.
Then you have other features that have their own business logic and similar depths behind the scenes when you need to make them work for 60 countries: - Airport pickup (lots of specific rules per region)
- Scheduled rides
- Commmuter card functionality
- Product types (there are SO many of these with special UI, from disabled vehicles, vans, mass transport in a few regions etc)
- Uber for Business (LOTS of touchpoints)
- On-trip experience business logic
- Pickup special cases
- Safety toolkit (have you seen it? Very neat features!)
- Receipts
- Custom fraud features for certain regions
- Customer support flows
- Regional business logic: growth features for the like of India, Brazil and other regions.
- Uber Eats touchpoints
- Uber Family
- Jump / Lime integrations (you can get bikes / scooters through the app)
- Transit functionality (seen it?)
- A bunch of others I won’t know about.
Much of the app “bloat” has to do with how business logic and screens need to be bundled in the binary, even if they are for another region. E.g. the UPI and PayTM SDKs were part of the app, despite only being used for India. Uber Transit was in a city or two when it launched, but it also shipped worldwide.
And then you have the binary size bloat with Swift that OP takes about.
Like in the app, "payments" sounds like it would be a simple Stripe integration but it isn't. For one, that's way too expensive at Uber's scale. They build their own payment integrations to save fractions of a point that adds up to tens of millions of dollars in saved expenses.
There's real time systems to facilitate the trip, then the order processing systems to process that raw data, hydrate it, store it in multiple data warehouses, and publish it to Kafka to be consumed by literally hundreds of other services. The main money pipeline involved many services covering things such as rider incentives, driver incentives, rider fraud, driver fraud, tax calculations, rider payments across all of those aforementioned payment methods, receipts, invoices, driver disbursements, rider support, driver support, and more.
Then take in to account that almost all of those services were written for a world designed only for taking passengers from point A to point B with Uber Black but have huge added bloat to accommodate:
* Uber X
* Uber Pool
* Uber Eats
* Uber Freight
* Uber Moto
* Lime
* Jump
and proof of concept work for all sorts of new business ventures like Uber Elevate and ATG. All of that cruft is expensive to maintain and all of those actual product lines started as experiments.
When I left in 2019, there were two versions of the vast majority of those systems in production as all were being rewritten to handle the significant changes to Uber's business model from inception to now.
Additional edit: These services also require massive numbers of server hosts. All of the old stuff was written in Python, all the new stuff is in Go. That alone is saving Uber tens of millions per year just in hosting costs.
Uber PMs and engineers should be super proud. The fact that each user thinks that their one use-case is "all the app does" is fucking brilliant product design.
I've had over a thousand Uber rides (used it to commute) in 10+ countries and it's obvious there the way it seamlessly adjusts to each region but even otherwise I think I could have guessed just from the difference between SF and Seattle. There was a sort of joy of opening the app in a new region and finding unique options. Some sort of (maybe intentional) superstar-level product design.
Rickshaws in Seattle? Cash payment in India? Uber Taxi in SF? Sweet!
Alternatively: The fact that they try to bundle all the world's use cases in some 100+ MB monstrosity of a client app is a tragicomic commentary on the misaligned incentives in the tech world's VC-driven wannabe-monopolies.
What happens when a user who travels unexpectedly discovers they need to download a new version of the app for where they are now? And what if they're on a low-bandwidth or capped cell plan and can't download a 100MB app easily, where 3/4 of the data is duplicates of stuff in their other 5 copies of the app?
What happens when a user tries to book an Uber from one area with a special app version to another area with a different special app version? This could easily generate tens of thousands of permutations.
What happens if the user is physically in one country right now, but is a national of another country and wants to pay for their Uber in their usual currency and payment method?
If the user ends up needing to have 20 copies of the app for various locations, how well will they sync ride history, changes in payment methods, and other such info?
I don't even work for Uber, and I thought of all of these complications in 10 minutes. The real Uber probably has 100x more complications that would arise from splitting location-specific functionality into location-specific apps.
It's not about creating 10,000 different permutations of an app, but downloading/installing content on demand.
If we are afraid that there might be problem that user later on has not enough bandwidth or issues like that, we'll start immediately downloading/installing after initial bundle in order we think this user might need them.
This means much faster initial usage, much less bandwidth used in total.
Same with developing experience. You shouldn't have to compile code that is not important for your development exp. This way compiling wouldn't take much time, dev productivity would be increased tremendously.
For Android purposes: maybe it's allowed in the Play store? I'm not sure tbh. There are certainly other stores with other rules (and Uber is in many of them too), or they could distribute it themselves (but we can see how well that has worked for e.g. Fortnite), but you still need to handle any scenarios where it's required.
In this case it could easily be reused within both Android and iOS without having to create duplicate business logic which I think already is a nightmare.
Airports logic I think should still be something that is defined using JSON/XML or w/e and algorithms should handle parsing that.
Or come on, there must be a way to get airports logic in an easier way. Does user have to update the app every-time when there's some minor airport update?
If it does, say you made a generic shim, you're effectively downloading and executing dynamic code. Maybe not by a technical definition, but that's not the point of Apple's rule, nor will they care about technical arguments when they reject it. There are plenty of examples of this happening in practice, I'm not talking hypothetically - it's why they have a carve-out explicitly for education apps. Even if you don't do stuff like that, and stay entirely in the webview, PWAs (unless it's actually being run in the browser, of course) are not allowed to make significant changes without going back through review.
edit: you can absolutely shrink your binary size with a PWA or similar though, yeah. There will definitely be other tradeoffs you have to make though, I'm not deeply familiar with those. Performance and integration with native libraries at the very least (e.g. map rendering).
Granted, you can/should strongly isolate them so it's infeasible for one module to interfere with another. But as always, YMMV in practice. And good luck testing them all either way.
Speaking of the defaults, the auto-suggests these guys have are fucking ridiculous too. Both Lyft and Uber. It's 4 AM in the morning and I've opened the app at home: offers me a ride to SFO. It's 11 PM in the evening on a Friday/Saturday and it offers me a ride to my friend's place. It's midnight and I'm at that friend's? They offer me a ride to my favourite club. I am one-click touching everywhere. These guys are good.
Superb user functionality.
On the other hand, the not so skilled ones...
Squares include:
* Uber being a simple CRUD app
* Uber having no moat because it can easily be disrupted by those able to build the app over a weekend, because Uber is a simple CRUD app
* Uber continuing to operate solely because VC's currently subsidize every single ride
Free space in the middle: drivers have no clue as to their costs and are only signing up because they're too naive to realize they're losing money.
Uber works relatively well in every country you go to, including at one point, China. Not many apps have ever accomplished that.
Uber made the right choice.
I'm from EU country and it was so convenient to be able to use Uber in NYC the same way as I would in my home town.
At this point it's really hard to believe the argument that "but our app is more complex that you could possibly understand" is valid. Compare your list with a Linux distribution from a handful of years ago: do you have more "screens" than that? Can you do more?
It's a relatively well known fact that multi-team corporate projects have tech debt. There was a thread recently about Google Cloud being slow due to too-many-cooks-in-the-kitchen syndrome, and many ex-amazon folks say AWS UI is notoriously difficult to refactor for similar reasons.
And there are cases, even in the Uber app, where thought has been put into optimizing both for upfront download size and internal complexity (the legal pages, for example)
Something that is worth considering is that a lot of what was said about the app being complicated is that it is also _dynamically_ complicated. For example, in the driver app, a feature was added to ensure drivers are wearing face coverings. The backend for that does AI-based fraud detection to prevent shenanigans like photos of photos. The picture upload flow is just one small part of a much more complex project that had to be rushed out because the covid pandemic happened. There are a bunch of similarly large impact things that touch several of the Uber apps simultaneously, often in non trivial ways and with tight timelines (e.g. features for compliance with specific legal requirements)
The reason I think Linux seems like a bad comparison is that if Linux contributors don't want to support some random driver for 3 years, that might be annoying but is generally just the way it is. Uber can't really afford to not do some of the complicated things it does in a timely manner.
I understand that Linux doesn't necessarily always have the product marketing deadlines or whatnot that Uber might have, but it's saddled with legacy code too. When things have security issues, or the API is just awful, at some point someone needs to fix it. Especially if your business is being decimated by a bug or you are lacking in APIs that meet your usecase. I don't think it's as poor as a comparison as you might thing it is.
Note how in this story the Android and iOS teams started a big rewrite at the same time, but the story consists exclusively of fuckups on the Apple size. Apple impose an arbitrary cellular download limit. Apple designed a language that can't handle more than 9 dynamic libraries before it hits scaling issues. Apple prevent you from using interpreters or other techniques to change the app after it's installed, unless you use a WebView in which case it's OK for mysterious reasons. Apple don't help even when major brands are weeks away from wrecking their business by hitting these limits. Apple Apple Apple. Meanwhile the Android guys are sitting pretty with Java and now a migration to Kotlin, which is (a) a much better language than Swift and (b) is dogfooded at large scale on the IntelliJ project so you know it scales (had no scaling problems with Kotlin and I've been using it for the last five years).
Same with dev experience. You shouldn't have to compile things you are not using to dev.
This would give pretty much infinite scaling.
I do think Uber requires tons of engineers, but I disagree that all of this has to be downloaded immediately or bundle size has to be this big.
All of what can be logic wise should be server side. For instance Airport logic seems like one.
Pretty bad UX, it's clear it wasn't designed for this use case, you have to go into Settings, but it works if needed.
But basically the only time I use Uber is when I travel to another region. I don't want 30 different Uber apps on my phone...
Apparently all this location-specific business logic has to be in the app itself and is not downloadable on demand.
Edit: https://news.ycombinator.com/item?id=25376346 goes into more details.
It doesn't have nearly as much functionality as gregdoesit mentioned, but it's enough to service the use case of getting you from point A to point B when you're stuck w/ crappy connectivity.
I've worked on similar problems where "everything is a special case" in some vein and it takes a few very clever minds to construct the algorithms and datastructures that can come to a good solution. But that's just it, it's the work of a few engineers and not thousands writing if-else blocks for each airport.
I still don't understand what Uber does that requires so many people getting paid so much money, the only thing that makes sense to me is they don't want them working for Lyft. It would be helpful for people to shed more light on how engineering resources are spent and what particular systems require so many hands to realize, but that's too valuable information to leak I'd imagine.
Now create a driver profile and do the same thing.
Now create the multitude of other special profiles they have and do the same.
Now do the same for Uber Eats.
Now do the same for Eats but using the restaurants version of the app.
And the couriers.
Now get in on Uber Freight (and any other projects they have) and do all of the same.
Now do all of that again for their other platforms (iOS, Watch OS, Wear OS, Android, Web)
Now access their internal tools that make all of this possible and do the same.
Now think about localizing all of that. Handling special cases for different places/languages/legalities. Think of all the different payment integrations. Security issues. Infrastructure. Technical debt. AB testing. Metrics. Dev tooling.
And I've only begun to scratch the surface here. The point is there is a lot more than meets the eye, especially when operating on a global scale.
Or am I crazy?
My point is that there is 100s of screens/features in the Uber app that you don't even know exist. And you have those same 100s of screens across their other apps. Multiplied across all of the different platforms. Plus any internal tools they use.
I'm curious, have you worked at a software company of Uber's scale? Not trying to be a dick here, genuinely curious because I hear these types of comments often here on HN and I wonder how many of those commenting have seen the scale from within.
"100s of screens" is as meaningless to me as "thousands of lines of code" and wholly uninteresting because it seems like the front end is the least significant reason why Uber requires so many people to operate. That's why I'm interested in seeing how many people are assigned to projects and what they actually spend their time doing, because at scale you begin to experience inefficiencies due to scale for bad reasons if your incentives aren't aligned. From the original twitter thread that sounds like it was rampant at Uber.
Not sure how long you've been around, but "Uber scale" used to be a joke a few years ago because no one could understand why they would reinvent so many wheels because "they don't work at Uber scale." The joke being that Uber didn't need to be Uber scale to begin with, they just had investor money to burn.
1. The amount of engineers scales very badly with respect to application complexity. I.e. you may need 100 or 1000 engineers to do something that "looks" about 2x as complex as something 10 engineers can do, and
2. The more complex solution tends to win in the market because on the one hand people value the application handling edge cases better, having shiny graphics, having slightly better UX, etc. and on the other hand you redeem the cost of paying those 1000 engineers thanks to software being free to copy and network effects / monopoly.
You're not answering the question except as a tautology:
- Q: Why is Uber's engineering team so ridiculous and bloated?
- A: Because they need a ridiculous bloated team to make this ridiculous bloated app.
Companies grow into new products, services, markets, etc.
Microsoft and Amazon have multiple profitable lines of business.
Is Uber more like Google or more like Amazon? Is "Uber Eats" (underpaid contractors drive around takeaway dinners) distinct from "Uber" (underpaid contractors drive around people), or "Uber Black" (underpaid contractors with unwise car leases drive people around)?
It's not like people would go and visit a page with just a list of ads; no matter how little revenue the other products around ads bring in, they bring eyeballs to the ads, and serve as a moat around ads.
It's not like people would go and visit a page with just a bunch of ads; no matter how little revenue the other Alphabet products bring in, they bring eyeballs to the ads, and serve as a moat around ads.
Having a large number of engineers is fantastic for making the company valued higher.
Directors love having more engineers under them.
Overall I've been highly speedy with 4 engineers, and slowly adding a 5th. Sure there are limits, but I think limits makes us think about being more efficient with how we build. It is an entirely different mindset.
Once you decide to "go big" you end up in this situation where you usually literally throw a dozen engineers at the problem and the problem goes away. I've seen designers spend 6 months on a _single page_. Because they can. and they get paid crazy money for it.
With an operating system UI, the developer may reasonably know what the UI will look like, what features are present, where they are placed. With Uber, a single person may not know all the rules for a single feature, and the rules change frequently, sometimes in the most obtuse ways. Like when San Francisco decided to ban cars from one of its most important artery streets earlier this year (market st). Now not only you need to indicate to a rider that this is a thing, your UI also needs to to provide walking instructions to the nearest designated pickup spot, and the routing algorithm needs to not put drivers on market st. Oh, and don't forget Hebrew speaking tourists read right-to-left and that food delivery bicycles _are_ still allowed on market st. That's just one change in one city.
Consider that just about anything might change anywhere, any time. From how earning breakdowns are displayed for drivers in Sao Paulo (this differs by jurisdiction), to where the pickup locations are in Pearson airport in Toronto (airport rules change all the time), to what promos apply in Mexico City this week (which depends on marketing and finance analysis), to whether surges should be disabled in response to a terrorist attack, to what documents a driver signup rep should be scanning into the system as local regulations change.
Now build all the infrastructure required considering that the people on the ground reporting their local changes are not programmers, and conversely many of these changes may require making changes to the UIs that these people use, how the new data is aggregated, etc. There are literally thousands of greenlight hub / customer support employees worldwide using these internal tools, many don't speak english and timezones are all over the place. The nature of problems are often fuzzy, hyperlocal-specific (e.g. certain forms of fraud), or extremely meticulous (e.g. legalese, etc).
The trouble of scale comes with having to deal with numerous nebulously defined things from numerous stakeholders and having to distill which engineering teams are even responsible for which aspects of which thing. As the number of stakeholders grow, so does the risk of miscommunication, unclear chain of responsibility and other well-documented large organizations challenges.
I don't reject that there are troubles in concerting real world data into a representation for the constraint solver. But this isn't untrodden ground, and there's no reason a front end developer should need to care about it. The problems you're describing are far behind the UI; and if they're not your organization has immense problems.
Well, yes and no. You're right that trying to express every nook and cranny of the business rules as solely a UI problem is a wrong way of trying to understand the complexity. But a frontend developer does need to care about how to surface whatever complexity needs to be surfaced, and likewise, backend developers need to know how to model the complexity, and there needs to be some level of orchestration between all the moving parts. Remember, Uber is built on a microservice architecture, and many things that would be arbitrary hardcoded values in projects elsewhere (e.g. pricing) are instead tied to complex dynamic systems in Uber.
In the pricing example, when Uber added the option for drivers to set their own multipliers, it wasn't just a new slider in the driver app. It also affected the pricing AI model, the matching algorithm, how and when prices are displayed in the rider app, geofencing rules, etc etc.
When new regulations mandate that every commit that makes production changes in your city must have an appropriate audit trail, when you need to deploy a cheat-proof face mask detection AI model in response to a global pandemic, when you need to figure out how to make customer support cost $1 per engagement instead of $10, when you need to figure out how to make your network protocol faster in Bangalore, when you need to figure out how to make GPS more accurate in Manhattan, or when any of hundreds of fuzzy problems need solving, then saying that variations of all of these problems have been seen elsewhere misses the point that going from problem statement to deployed solution isn't always a trivial task.
The original question was why so many people work at Uber. The answer is that there are a lot more fuzzy problems than meets the eye.
I never work for Uber but I can easily imagine the following problems:
* Map data is not a solved problem. Tons of inaccurate data especially on non-english speaking countries.
* As someone mentioned about payments, different country requires different payment platform integrations.
* Payment is just one aspect of money, you need to build a treasure-like logic to run a marketplace between drivers and passengers.
* The matchmaking algorithm itself is interesting enough problem. It has to be efficient for the company to make the most amount of money.
* Tons of internal data are produced and required to be understood. This requires ML/data analytic talents.
Most of what you mentioned should be server side.
I mean, you could push some of that to the client, but I'd understand why your app is gigantic and doesn't do app-ly job very well with all the background processing. And I'd be surprised the app hasn't been hacked to get at the analytics data, if not the payments.
> The app looks simple from the user perspective. But on the global scale the business rules are insanely complicated. Every region has custom rules/regulations. Different products have different workflows. Some regions have cash payments. Every airport has different pick rules...
A more positive take: if you look at the specifics of how many regions Uber is (was) in, and that each region has cities each of which have wildly different regulatory environments, you can imagine the permutations of complexity they had to deal with. Then you add in growing internal business experiments like Eats or mobility (Jump) and it's easy to see how this balloons out of control pretty fast.
It justifies the employment of 400 iOS engineers, it also lets you book taxi rides.
Apparently with Uber it’s the infrastructure design and back-end algorithms for trip routing and driver management that are “so great.” The SV hype machine helps a lot too.
They also have a huge need for speed. Shipping faster and winning a market is worth 10s of million in annual revenue and billions in valuation.
A lot of this is resume driven development as well. Solving a hard problem, even if it's an invented one, looks good on your resume. So does using the latest and greatest tech stack instead of a boring but functional one.
I appreciate the difficulties of getting an app to fit in a fixed space having worked on a point of sale system that had to run on a MIPS device with 32MB of Flash, of which half was occupied by Windows CE. It's not really clear to me why Uber's app needs to be so large and complicated other than the availability of a large amount of money, high-powered engineers, and shiny new unproven technologies.
(The compact app fit in about eight megs in the end, plus another meg or so for the file which defined all the screens; it was written in the least trendy, unsexiest framework known to man, Windows MFC in C++)