The Difficulty Cliff: Why Building Software Has Never Been Smooth (Until Maybe Now)

How every abstraction in software creates walls, and why AI might finally demolish them
The learning curve for building software is a lie. It’s not a curve at all—it’s a jagged mountain range with sheer cliffs where the trail suddenly ends.
You’re cruising along with your no-code tool, dragging and dropping your way to success. Everything’s great until you need that one custom calculation, that one specific integration, that one behavior the platform didn’t anticipate. Suddenly you’re not climbing anymore—you’re standing at the base of a cliff, staring up at “real” programming.
Welcome to the difficulty cliff. After so many years building developer platforms, I’ve come to believe these cliffs aren’t bugs—they’re the fundamental challenge of software abstraction. Every tool that makes development easier eventually hits a wall where its abstraction breaks down.
But what if AI could finally smooth this curve? Not by building better walls, but by eliminating them entirely?
The Painted Tunnel Problem
Picture yourself as Wile E. Coyote, running full speed through the desert of software development. That tunnel ahead? The one that looks like it goes straight through the mountain? It’s just paint on a cliff face. You won’t know until—SMACK—you’re flattened against the rock, sliding down cartoon-style. That’s the infrastructure ladder in a nutshell.
Let’s map the entire difficulty curve, from “anyone could build this” to “I need a PhD in distributed systems.”
Start at the bottom: No-code platforms. Drag, drop, done. You’re building apps with visual interfaces and pre-built components.
Move up: Low-code platforms. Now you’re writing formulas, maybe some light scripting. You’ve graduated from pure visual building to some logic.
Next cliff: Frameworks and libraries. Suddenly you need to understand React, authentication flows, state management. You’re writing actual code, but the framework is holding your hand.
Keep climbing: Managed cloud services. Firebase, Lambda, DynamoDB. You’re composing services, but the provider handles scaling, patching, and operations.
Then: Container platforms. Kubernetes, ECS, Cloud Run. You’re packaging applications, defining resource requirements, managing orchestration. You own the app; they own the platform.
Higher still: VMs on managed hardware. You’re provisioning virtual machines, managing operating systems, dealing with networking and security groups. The cloud provider handles the physical layer, but you own everything above it.
The summit: Bare metal in a data center. You’re managing physical servers, network hardware, cooling systems. You’ve gone from “I want to build an app” to “I need to understand thermal dynamics.”
Each step isn’t just incrementally harder—it’s a completely different mental model. Moving up means needing to understand meaningfully more complexity and breaking through all the assumptions that your previous abstraction relied on.
And this is just one dimension. There are orthogonal difficulty curves everywhere—frontend complexity, data modeling, security, performance optimization, team coordination. Each has its own cliffs. But this infrastructure ladder is a perfect example of how what should be a smooth progression becomes a series of walls.
Here’s the thing: modern applications span this entire stack, but as a developer, you only deal with the level you’ve chosen and everything above it. Pick Firebase? You’re working at the managed services level while all the container orchestration, VMs, and hardware complexity is handled for you. Until it isn’t—until you need something your abstraction doesn’t provide. Then you face the cliff.
Software’s Dirty Secret: It’s All Cliffs
We like to pretend software development is a continuous spectrum from “easy” to “hard.” It’s not. Good platforms and frameworks smooth their own little sections of the difficulty curve, but they’re surrounded by cliffs on all sides.
It’s worth noting that abstractions create these walls, but they also make building bigger and more sophisticated things tractable. Without abstractions, we’d never build anything complex. The problem isn’t the abstraction itself—it’s the cliff you hit when you outgrow it.
Start with no-code platforms. Brilliant for what they do—until they aren’t. Need custom logic? Cliff. Want to integrate with an unsupported API? Cliff. Have performance requirements? Cliff.
The cruel irony: the better the platform, the higher the cliff when you outgrow it. Your users are less prepared for the jump because the platform has been protecting them from complexity. They’ve built their entire mental model around the platform’s abstractions, and now they need to throw it all away and start over.
I lived this at Parse and Firebase. We built beautiful abstractions that made backend development accessible to mobile developers. One line of code to save data. Another to authenticate users. Magical.
Until it wasn’t.
Parse: Great Until It Wasn’t
At Parse, before we added Cloud Code, the platform was almost impossible to take seriously for production apps. You couldn’t run trusted code—no server-side validations, no operations requiring secrets, no sophisticated permissions. You had to trust every client to be a good actor.
When developers inevitably needed these capabilities, they faced a brutal cliff: suddenly needing to spin up and run their own services on AWS. This was before Lambda, before easy auto-scaling. It was expensive, complex, and completely different from the mobile development they’d been doing. You went from being a frontend developer using Parse’s backend to suddenly needing to learn to build and operate your own backend infrastructure.
Cloud Code made that cliff smaller, but it didn’t eliminate it. We still bled users out the back end as they outgrew what we could provide. They’d hit scaling limits, need more sophisticated queries, find our pricing didn’t match their usage patterns, or require compliance features we didn’t offer.
Here’s the brutal economics: once a customer had to build any backend infrastructure to handle what we couldn’t, they’d often migrate everything off our platform. Why maintain two systems? Once you’ve already paid the cost of hiring backend engineers or learning AWS, might as well own it all.
This, in my mind, is one reason the Firebase acquisition by Google worked far better than Parse’s acquisition by Facebook. When Firebase users outgrew the platform, there was an entire Google Cloud ecosystem to graduate into. When Parse users outgrew us, they hit a dead end.
The Authorization Trap
Take authorization—every app needs it, but every app needs it slightly differently. Parse chose role-based access control (RBAC). Firebase created a custom rules language. Both worked beautifully for their sweet spots. Both created cliffs for everyone else.
Parse users who needed more sophisticated permissions could write custom validation in Cloud Code, but it was a kludge with its own limitations. Firebase users who outgrew the rules language would often wrap their database calls with backend code, but then they’d lose the simplicity and real-time advantages of working directly from client code—defeating much of Firebase’s purpose.
This is the “right holes” problem: platforms have to guess which escape hatches developers will need. Sometimes we guessed right. Often we didn’t. And every wrong guess meant another developer contemplating whether to abandon months of work or attempt to scale the cliff.
Why Everyone Picked a Lane
These cliffs are why we have “frontend developers” and “backend developers” and “DevOps engineers” and “ML engineers.” Each specialization is really just a group of people who’ve learned to climb specific cliffs.
Don’t get me wrong—sometimes specialization makes sense. At Firebase, having iOS, Android, and web specialists helped us build the right developer experience for each platform. At Google Cloud, having Ruby and .NET experts means we can go deep on what those communities need.
But for most teams building most products? Frontend/backend specialization is pure overhead. It adds zero customer value. It’s a symptom of difficulty cliffs, not a feature. The longer you can get by with generalists focused on what makes your product valuable, the better.
AI’s Trick: It Gives You Actual Code
Here’s what’s different about AI-powered development: it generates actual code, not configuration.
When you outgrow a no-code platform, you’re stuck. The “application” you built is locked in their proprietary format. You can’t extend it—you have to rebuild it.
When you build with AI assistance, you get real code. TypeScript. Python. Whatever you need. When you hit a limitation, you’re not facing the same kind of cliff. AI still gets stuck—we’re not at a gentle slope yet—but it stands a far better chance of helping you work through the problem. And critically, it leaves you empowered to take the reins when necessary. You’re not abandoning your work; you have the option to evolve it.
That Time I Built Something With My Parental Leave Brain
During my recent parental leave, I experienced this firsthand. I wanted to create simplified vector art that could be 3D printed in multiple colors for my kids’ bedrooms. I needed a tool that could convert images to vectors, quantize colors intelligently, and prepare files for printing.
I’m not great at building web UIs. I don’t know much about color theory and quantization algorithms. I’m definitely not a Docker expert.
I started by having AI build a command-line script to automate what I’d been doing manually. Then I used AI to iterate on it—adding a web UI, implementing sophisticated color processing, making it self-hostable with Docker. I did all of this in the time it used to take me to manually prepare one image. The results far exceeded what I could have produced alone.
Is the code beautiful? No. Is it perfect? Definitely not. But it works, and—critically—I’m confident that if I wanted to productize or productionize it further, these same tools would help me continue down that curve. The cliffs became climbable.
Each traditional cliff—UI development, algorithmic complexity, DevOps—became navigable with AI assistance. Not smooth yet, but dramatically less steep than before.
Technical Debt Is Only Debt If You Have to Pay It
“But wait,” you’re thinking, “isn’t all this AI-generated code just accumulating massive technical debt?”
Here’s the thing: technical debt is only debt if it ever comes due.
Yes, AI might generate code in patterns that aren’t ideal for scale. Yes, you might accumulate inconsistencies faster. Yes, you might not fully understand every line.
But AI is also getting better at refactoring its own output, explaining what it built, and migrating to better patterns. The same tool that helped you build into a corner can help you build your way back out. And these capabilities are improving every day.
The curve isn’t smooth yet—but it’s getting smoother over time.
We’re All Engineering Managers Now
This changes what it means to be an engineer. Every engineer now needs to learn the skill of orchestrating AI tools and understanding the codebases they produce. It’s less about deep expertise in specific technologies and more about:
- Knowing how to break down problems for AI to handle effectively
- Understanding enough about each domain to spot when AI is leading you astray
- Reading and comprehending code faster than you write it
- Having the taste to know what “good enough” looks like versus what needs refinement
This is the kind of thing I coach my engineers on routinely as an engineering manager—operating at the altitude of “what problem are we solving” rather than “how do we implement it.” The ability to increase altitude, think about the value you’re trying to add rather than the specific implementation details, is a hallmark of both effective senior engineers and good engineering managers.
Plot Twist: Platforms Win Harder
Here’s the counterintuitive insight: platforms become MORE important in an AI world, not less.
Good frameworks, libraries, and APIs will be key to guiding AI agents into the pit of success. Without them, AI will generate a thousand slightly different implementations of the same thing, each with its own quirks and bugs. There’s no reasonable way for AI to learn from that chaos.
But with good platform abstractions? AI can recognize patterns, follow established practices, and generate code that’s consistent and maintainable. The platform provides the rails that guide AI toward good outcomes.
This isn’t about platforms trying to predict every escape hatch—it’s about platforms providing solid foundations and proven patterns that AI can build upon and extend.
Tools We Haven’t Invented Yet
What might the future hold? We don’t know yet exactly what tools will emerge, but here are some possibilities that could help smooth these transitions:
- Migration assistants that help AI understand both sides of a transition (“I see you need custom auth logic, let me help you transition from Firebase Auth to a hybrid approach…”)
- Documentation designed for agents, not just humans, that guides AI toward successful patterns
- MCP servers that suggest paths based on what others have successfully built
- AI-based approximations for fuzzy logic that platforms struggle to codify
- Continuous feedback loops where agents surface the actual patterns developers are taking
We might even set agents to the task of analyzing where developers get stuck and helping us build better extensibility. Finally, we could see the actual paths developers are trying to take instead of guessing where they need help.
The Future Is Smoother (Eventually)
Imagine a world where the difficulty curve is actually… a curve.
You start describing your need to AI: “I need an app that tracks my workout progress.” AI asks clarifying questions—what platforms, what kind of workouts, how do you want to track progress—and uses your answers to guide itself toward a working prototype. You refine: “Add social features so I can compete with friends.” The code evolves. You get specific: “The leaderboard calculation should use this exact formula.” The AI helps you modify that specific function.
We’re not there yet. AI still gets stuck. The curve still has cliffs. But they’re getting smaller, and more importantly, when you hit them you have options beyond “start over” or “hire a specialist.”
This isn’t about making programming obsolete—it’s about making it continuous. The person who started with “build me an app” can gradually learn programming concepts as they need them, in context, with working examples from their own project.
What Platform Builders Should Actually Do
For those of us building developer tools and platforms, the message is clear: identify your cliffs, build paths through them, and think about how AI could help smooth the journey.
We’re still learning how to do this. But some principles are emerging: Your abstractions should be permeable. Your simplifications should be peelable. Your conveniences should be optional. And most importantly, think about how AI agents will navigate your platform. What patterns do you want to encourage? What guardrails keep them on the path to success?
The winners won’t be platforms that make easy things easier—they’ll be platforms that make hard things approachable and guide both humans and AI toward successful outcomes.
After two decades building platforms—from Microsoft to Parse to Firebase to Google—I’ve never been more optimistic or excited about what’s possible. We’re not eliminating barriers overnight, but we’re finally making real progress on smoothing them out.
The age of difficulty cliffs isn’t over yet. But the age of progressively smoother curves is beginning.
And the platforms that recognize this shift—that embrace their role as guides rather than gatekeepers—will define the next era of software development.