~10 min read · You asked for a login. Your developer said three weeks. You didn't understand why. Here's what's actually in that estimate — and what the size of it tells you about your app.
It usually goes something like this.
You're looking at your app, and you realize something obvious is missing. There's no real login. Users sign in with their email each time, or there's a guest mode that's been "temporary" for months, or the auth is half-finished from launch and nobody's circled back to it. You ask your developer to add a proper login. Email, password, "remember me," forgot password, the standard things every app has.
You're expecting a number that sounds like one feature. Two days. Maybe three.
The answer comes back: two and a half weeks. Maybe three. There's a careful explanation that includes phrases like "session management" and "we'll need to update the API", and you nod through it, but somewhere underneath you're thinking: it's a login. Every app has a login. Why is this taking three weeks?
This is one of the most common conversations in the life of an app, and it's one of the most misread. The developer isn't padding. The estimate isn't crazy. But the size of it is telling you something important about your app — and once you know how to hear it, this conversation becomes one of the most useful diagnostic tools you have.
"Just Add a Login" Is Not One Feature
The first thing to understand is that "login" sounds like one thing and is actually about twelve.
It is, in roughly this order: the sign-up screen, the login screen, the password-reset flow, the email verification, the token storage, the token refresh logic, the session expiry handling, the "remember me" behavior, the way every other screen in the app checks whether you're logged in, the way the backend validates the token on every request, the way the app handles the moment the token becomes invalid (which is not the same as the moment it expires), and the way the user is sent back to the login screen when that happens — preserving wherever they were trying to go.
This list isn't exhaustive, although it might be exhausting. It doesn't include things like rate limiting on failed attempts, account lockout after too many failures, social login providers, two-factor authentication, password strength rules, or "sign in with Apple" (which Apple now requires if you offer any other social login). And it doesn't include the bug class that lives under all of this: race conditions between when the token refreshes and when the app tries to use it.
Each of these twelve-or-more items is small. Most of them are well-understood. A competent developer has built most of them before. But each one touches a different part of the existing app, and that touching is where the actual work lives.
This is the first thing the estimate is telling you. The feature you asked for has twelve sub-features. The developer is not adding one thing; they're adding twelve, and connecting each of them to whatever was already there.
What the Login Actually Touches
Here's what almost no estimate spells out, and what almost everyone underestimates.
The login flow is one of a small handful of features that has to talk to every other feature in your app. Every screen that loads data has to know whether the current user is allowed to load it. Every action that changes data has to know who's performing it. Every API call has to carry the right credential. Every offline cache has to be scoped to the right user, and emptied when the user logs out, and not leaked to the next user who logs in on the same device.
In a well-structured app, this is all handled in one place. There's a single layer that knows about authentication. Every screen, every API call, every cache asks that layer. When the developer adds the new login, they update the one layer, and everything else inherits the change automatically. The twelve sub-features fit in cleanly because the structure was waiting for them.
In a poorly-structured app, authentication is scattered across the codebase. One screen checks login status one way; another screen checks it another way; a third screen forgets to check at all. The token is stored in three places. The "log out" button only clears two of them. Every screen has its own assumptions about what it means to be logged in, and most of those assumptions were never written down.
When the developer adds the new login to a poorly-structured app, they don't just add the twelve sub-features. They also have to find every place in the existing codebase that touches authentication and update each one. That's the part that turns a three-day feature into a three-week project. They're not building a login. They're building a login and refactoring authentication across an app that didn't have a coherent authentication story to begin with.
The developer almost never explains it this way, because explaining it this way feels like blame. So instead they say
"it's complicated"
or
"we need to update a few places".
Both of those are true. Both of those are also code for
"your app has technical debt, and this feature is the moment we're paying some of it"
Why "Just" Is the Most Expensive Word in Software
The word "just" appears in almost every painful conversation about feature cost. Just add a login. Just support multiple languages. Just add a notification when X happens. Just let me see this on the web too.
"Just" is a word that means "this feels small to me". It's an honest report of how the request lands in the buyer's head. The login button is one button; surely the work is similarly small. The notification is one notification; how hard could it be.
The trouble is that "just" describes the surface area, not the actual work. The surface area of a login is one screen and one button. The actual work is the twelve sub-features and the structure they have to fit into. The surface area of "support multiple languages" is replacing some strings. The actual work is making sure every screen handles strings that are 30% longer in German, every date format respects the user's locale, every error message has a translation, every email template has a translation, every push notification has a translation, and your backend remembers each user's language preference and uses it everywhere. The surface area of a single notification is a popup. The actual work is the entire system that detects events, decides who should be notified, formats the message, handles delivery failures, respects the user's preferences, and remembers what's been delivered so it doesn't get sent twice.
This isn't because developers love complexity. It's because the feature you see is the tip of a small iceberg, and the iceberg has to actually be built.
A useful habit, when you find yourself reaching for the word "just," is to pause and ask: what would I be assuming about the existing app if this really were small? In a well-structured app, it might really be small. In an app that's been growing without a plan, it almost certainly isn't.
The Estimate as a Diagnostic Tool
Here's the part that turns the "just add a login" conversation from frustrating to useful.
The size of the estimate isn't just a number. It's a measurement of the structure underneath your app. A well-built app and a tangled one will produce dramatically different estimates for the same feature, and the difference isn't because one developer is faster than the other. It's because one developer is working with the structure and the other is working against it.
A login that fits cleanly into the structure takes three to five days. A login that has to be threaded through a codebase with no authentication layer can take three weeks, and the developer isn't wrong to quote that. They're being honest with you about how much work the feature actually requires in your specific app.
This means the estimate is a diagnostic. Two questions are worth asking when one comes in higher than you expected.
The first:
What about this feature is taking the time?
A developer who has been honest with themselves will tell you specifically. "About a day is the screens themselves. About a day is the backend changes. The other ten days is finding every place in the existing app that has its own idea about authentication and bringing it in line." That answer is exactly what you want, because it tells you both what the feature costs and what your app's underlying state is.
The second:
If we did this feature, would the next auth-related change be cheaper or about the same?
This is the question that separates surgery from patchwork. A good answer sounds like: "Cheaper. We'd be putting in the auth layer that the app should have had from the start, and after this, everything that touches login gets easier". A worse answer sounds like: "About the same. We're just adding the new flow alongside the existing ones". The first answer means you're paying down debt. The second means you're paying the same debt over and over.
This second question is the most underused diagnostic in a buyer's toolkit. Most app owners don't think to ask it. The ones who do find out very quickly whether they're building on a foundation or on top of one.
When the Estimate Is Telling You Something Bigger
Sometimes the estimate for "just add a login" comes back at a number that goes beyond surprising into alarming. Six weeks. Two months. "We should probably talk about this on a call".
When that happens, the feature is no longer telling you about itself. It's telling you about the app.
This is the moment to stop and notice. A six-week login is not just a one-time difficult addition, but rather a signal that the structural cost of changing your app has crossed a threshold where nearly all features are going to feel like this. The login is just the one you happened to ask for. The next request — payments, multi-user, role permissions, anything that touches multiple parts of the app at once — is going to come back at a similarly inflated number, for the same underlying reason.
This pattern is the heart of what we call getting stuck in technical debt. When small requests start producing large estimates, the structure underneath has reached the point where almost any change is expensive. The fix isn't to keep paying these inflated prices feature by feature. The fix is to address the structural problem directly — usually by improving the joints between the parts of your app rather than rebuilding the parts themselves.
What that intervention looks like, and how to know whether your app needs it, is the subject of a longer conversation. The short version is that it's almost always smaller than a full rewrite and almost always cheaper than continuing to pay the inflated per-feature cost for the next year.
What a Healthy "Just Add" Conversation Sounds Like
You don't need to be a developer to recognize a healthy estimate when you hear one. There are a few things that distinguish it.
A healthy estimate has a structure. The developer doesn't just give you a number; they tell you what the number includes and what it doesn't. They distinguish between the new feature itself and the work to integrate it. They mention specific risks ("if we discover X, that's another two days; if we don't, it's done") instead of hiding everything in a buffer.
A healthy estimate gets cheaper over time, not more expensive. If the second feature of a given kind costs the same as the first, the developer is patching rather than building infrastructure. If the second one is dramatically cheaper, you're investing in your app rather than just paying for features.
A healthy estimate is met with curiosity, not defensiveness, when you ask follow-up questions. A developer who's comfortable with the work is happy to explain it. A developer who isn't will deflect.
And a healthy estimate, importantly, can be high. It's not the case that "good developers always quote low". A high estimate from a developer who can explain why it's high is far more trustworthy than a low estimate from someone who just wants the work. The number isn't the signal. The explanation behind the number is.
For more on how to read these documents in general — what should be in an estimate, what's usually missing, what to ask before signing — our piece on how to read a developer's estimate goes deeper. The "just add" conversation is one specific case of the broader skill of estimate-reading. The skill is worth developing. It pays for itself the next time someone tells you a button is going to take three weeks.
What to Do When You Hear "Three Weeks for a Login"
The honest answer is: don't panic, and don't approve it without asking.
If the estimate makes sense once your developer explains it, the right move is usually to go ahead — and to ask whether this feature is a good moment to also fix the underlying structure that's making it expensive. Often, paying for the feature and the structural improvement together costs only slightly more than paying for the feature alone, and the next ten features get materially cheaper as a result.
If the estimate doesn't make sense, the right move is a second opinion. Not to second-guess your developer — to calibrate. An outside reviewer can usually look at the request and the codebase together and tell you, within a day or two, whether the number reflects the work or whether something else is going on.
And if you find yourself in the "every small feature has become a big project" pattern — not just for the login, but consistently — you're past the point where individual estimates are useful. You're looking at a structural problem, and the next step is a structural conversation. We cover what that looks like in the longer piece on technical debt, along with the options that exist beyond "patch each feature individually" and "throw the whole thing away."
The Real Cost of a Small Request
What you came in asking for was a login. What you got was a window into the structure of your app.
The estimate told you that the login is a twelve-part feature, not a one-part feature. The shape of the estimate told you something about how cleanly the parts of your app fit together. And the developer's explanation — clear or vague, structured or hand-wavy — told you something about how well they understand what they're working in.
None of this is bad news. The "just add a login" moment is one of the most useful inflection points in the life of an app, because it's the moment where you stop seeing your app as a set of screens and start seeing it as a structure. Once you can see the structure, every decision about it becomes easier. The features you want next either fit or they don't. The cost either reflects the feature or it reflects something else. The developer is either solving the right problem or papering over a deeper one.
You don't need to be technical to make these distinctions. You just need to know that the conversation about a small feature is usually a conversation about something bigger — and that the something bigger is the part worth paying attention to.