A warm, painterly oil painting of a chess board on a wooden desk near a window. Several chess pieces are positioned mid-game with translucent curved arrows showing a sequence of three moves. Golden afternoon light streams through the window, with a cup of tea and a leather-bound journal nearby.

The Three-Move Problem

I was on a video call last week with a mentee who had just finished laying out her plan. She wanted to change her role, change her team, and change her country. All at once. All within the year. She had this energy that I recognized immediately, the kind that comes from finally knowing what you want and not wanting to wait another minute to get there.

She was less than a year out of school, working in a sales engineering role at a large tech company. Her undergraduate work was in electrical engineering, and she’d done several internships in the healthcare technology space. She loved that work and wanted to get back to it. Specifically, she wanted to move into a full-time software engineering position, on a health-focused product team, in a different country. Three big changes, stacked on top of each other like a wish list written on a napkin.

I get it. I’ve felt that same pull. But I’ve also learned, sometimes the hard way, that trying to change everything at once is one of the surest ways to change nothing at all.

The Napkin Problem

Here’s the thing about goals that try to do too much at once: they aren’t really goals. They’re dreams. And I don’t say that dismissively. Dreams are important. But a dream without a plan is just a source of frustration.

There’s a saying I first heard from early SREs at Google that has stuck with me ever since: “Hope is not a strategy.” They meant it about production infrastructure. As in, “I hope the service doesn’t fall over during a traffic surge” is not a valid entry in your runbook. But I’ve found the phrase applies just as well to careers. “I hope all three of these changes work out simultaneously” is not a career plan.

When I asked her to think about what was really being asked of each of those changes, the picture got clearer. Changing from sales engineering to software engineering means proving yourself as a developer, building a portfolio of work, and earning the trust of an engineering hiring manager. That’s a significant lift on its own. Changing teams means building relationships with a new group, learning a new domain, and demonstrating relevance to that team’s mission. And changing countries involves visas, relocation logistics, and finding a manager in a different office willing to sponsor the move.

Each of those is a meaningful project. Stacking all three together doesn’t just triple the difficulty. It creates dependencies and friction between the moves that make the whole thing fragile. One delay in the visa process stalls the team change. One rejection from the health team stalls the role change. The goals aren’t S.M.A.R.T., and that sets us up for disappointment.

Sequencing the Moves

What I suggested was something simpler, but not easier: make a plan, and sequence the steps.

The first move would be to transition from sales engineering to software engineering locally. She was already doing real engineering work in her current role, building demos, writing integration code, debugging customer issues in production systems. Sales engineering isn’t a non-technical job, it’s a different job family. The gap wasn’t in her ability, it was in how that work was recognized. The key was to make her engineering contributions visible in the right context. Start picking up tasks on the engineering backlog. Get her code into the codebase, not just into customer-facing prototypes. Build credibility with the people who would eventually be her peers.

I also suggested she build a relationship with a nearby engineering team and start contributing to their work in her off hours. Those contributions go a long way towards building trust, and if she does good work for them, the next time they have headcount available, she’ll be the person they think of. It’s extra effort, and it means your work-life balance shifts for a while. But there’s a difference between an unsustainable grind and a conscious, temporary investment in a plan you believe in. When you’re making a change on your own terms, that extra work doesn’t feel like a burden. It feels like building something.

The second move, once she was established as a software engineer, would be to get closer to the health space. There were health-adjacent teams in her local office. Not the exact team she wanted to join, but teams working on things like fitness tracking or cloud services for healthcare providers. They weren’t her destination, but they were a lot closer than where she was now. This might even be a two-part move: first into an SWE role on her current team, then a lateral transfer to one of those local health-adjacent teams. With an engineering role already in hand, that lateral move becomes a natural step rather than a leap. And her internship background in healthcare technology becomes an asset in the conversation, not a footnote on a resume that’s asking for too many things at once.

The third move, once she was on a health-focused engineering team with a track record, would be the international transfer. At that point, she’s not asking a manager in another country to take a chance on someone changing roles, changing domains, and relocating all at once. She’s a software engineer on a health team who wants to continue that work in a different office. That’s a much easier conversation.

Each step captures real benefit along the way. After the first move, she’s a software engineer. That’s not a waypoint, that’s a career milestone. After the second, she’s working in healthcare technology, which is what she’s passionate about. The international move becomes the capstone, not the foundation.

The Long Game

I shared my own experience with her, because I think it helps to hear that this kind of sequencing isn’t just advice I give. It’s how I’ve navigated my own career. When I decided to move from leading large teams back to an individual contributor role, I didn’t just resign from management one morning and start writing code the next. It took years.

I started by slowly shrinking my management scope, which gave me more time for technical work. I began contributing to open source projects, rebuilding muscles that had atrophied during years of calendars full of meetings. Then I moved into technical projects at work, building credibility as a builder alongside my leadership responsibilities. Only after I had demonstrated that I could do the work, and that I wanted to do it for the right reasons, did I make the formal transition.

It wasn’t fast. But at every stage I was capturing benefit. I was learning, contributing, and building the case for the next step. And because I had a plan, the waiting didn’t feel like stalling. It felt like progress.

Goals That Actually Work

The temptation to change everything at once comes from a good place. It comes from clarity about what you want. But clarity about the destination isn’t the same as clarity about the route. The best career plans I’ve seen, both in my own life and in the lives of the people I coach, are the ones that break a big ambition into a sequence of achievable moves, each one valuable on its own.

This is actually where our conversation started. Before we talked about sequencing or strategy, I asked her one question: “What needs to be true to allow you to make this move?” That’s the question that unlocks everything. Write down where you want to be, then work backwards. What needs to be true before you can get there? What needs to be true before that? Keep going until you arrive at something you can start doing next week.

My mentee left our conversation with the same ambition she walked in with, but now she had concrete goals and a sequence of moves to get there. Step one was already something she could act on Monday morning. That’s the difference between hope and a strategy.

A spotlight shines on a pianist intensely playing a small, worn piano on a large, dark stage.

The Koln Concert and Creative Constraints

This week I was reminded of a story I like to tell, and the value of constraints on creative work. When I’m working, I often set my constraints before I begin. For example, on an old agentic coding project, I set a few constraints: “The orchestration model must be Gemini Flash,” “All tool calls are through sub-agents,” and “Permissions and configurability are at the core of the agentic loop.” From that, I ended up with adh-cli, a policy-aware TUI for working with Gemini that inspired many of the features I worked on in Gemini CLI last year. The project itself is defunct now and not maintained, but the constraints gave me a great way to think about the project and forced creativity in other areas.

We run into constraints in many different ways. Maybe it’s time pressure: How many of you felt like you wrote your best papers 24 hours before they were due? Maybe it’s the environment, like you must integrate with a certain piece of software, or you have to design your system in a certain way. Maybe it’s self-imposed like my example with adh-cli.

Or maybe the constraint is philosophical. Take Mario Zechner’s Pi Agent, for example. In a blog post, Zechner expressed frustration with the bloat of modern AI coding assistants that try to do everything, describing them as “spaceships with 80% unused functionality.” In response, he built Pi around an “anti-framework” philosophy of radical minimalism. He intentionally constrained his default coding agent to just four fundamental tools: read, write, edit, and bash. By stripping away the hidden system prompts and unpredictable context injections, the tool forces developers to be intentional. It proves that you don’t need a massive, opaque framework to build highly capable AI workflows—sometimes, fewer tools create a sharper focus.

Whether it’s a self-imposed architectural rule or an anti-framework philosophy, these software constraints force us out of our default habits and into a space of deliberate, intentional design. Yet, in our day-to-day work, constraints are rarely celebrated. In fact, that is actually how I end up in constraint conversations the most often: people don’t like their constraints because the constraint has been imposed on them externally. They see it as a restriction instead of a way to channel their creativity. To me, a constraint means that we shut down a huge portion of the exploration space. I don’t have to worry about a million different architectural choices because the constraint has made the decision for me. It is incredibly freeing. Whenever I try to help someone turn around their mindset—from fearing or being frustrated by constraints to being excited by them—I inevitably end up telling them the story of Keith Jarrett and the 1975 Köln Concert.

In 1975, a 17-year-old jazz fan named Vera Brandes organized a late-night concert at the Cologne Opera House. She managed to book Keith Jarrett, one of the most notoriously perfectionist jazz pianists of his generation. It was an ambitious undertaking, and almost immediately, it turned into a disaster.

Due to a backstage mix-up, the venue provided the wrong piano. Instead of the premier concert grand Jarrett requested, he was presented with a small rehearsal model. It was horribly out of tune, the pedals stuck, the high notes sounded tinny and harsh, and the bass lacked any resonance. Jarrett, exhausted and suffering from back pain, flat-out refused to play. It was only when Brandes followed him out into the pouring rain and begged him that he relented, taking pity on the teenager. “Never forget,” he told her. “Only for you.”

What happened next is legendary. Forced to play an unplayable instrument, Jarrett had to completely abandon his usual style. Because the high and low registers were awful, he confined his playing strictly to the middle of the keyboard. Because the piano was too quiet to fill the 1,400-seat opera house, he stood up and hammered the keys with immense physical force. To make up for the lack of resonance, he relied on rolling, repetitive, hypnotic rhythmic patterns in his left hand.

He embraced the limitations, and in doing so, he produced absolute magic. The recording, The Köln Concert, went on to become the best-selling solo jazz album in history.

I think about the Köln Concert all the time, especially lately as we navigate the current landscape of Artificial Intelligence and software architecture.

The Bloat of Infinite Resources

In modern software engineering, we are rarely handed a broken piano. We operate in an era of perceived infinite resources. Cloud computing gives us endless horizontal scaling. Context windows for Large Language Models have ballooned from a meager 4K tokens to 1 million or more. If an application is slow or an agent isn’t performing well, the default instinct is to throw more compute, more memory, or a larger model at the problem.

But infinite resources often breed intellectual laziness. When you have a 1-million token context window, you don’t have to think critically about what information actually matters. You just dump the entire codebase or the entire library of documents into the prompt and hope the model figures it out. It’s the equivalent of having a perfect Bösendorfer grand piano and just mashing all the keys at once.

A pragmatic engineering manager might push back here: Developer time is expensive. If I can solve a problem today by dumping an entire codebase into a 1-million token context window, isn’t throwing compute at it just good business?

It’s a fair question, and engineering is always about tradeoffs. But the tools have evolved—building a RAG pipeline doesn’t take a week anymore; with the right utilities, it takes minutes. More importantly, relying on infinite resources often hides long-term costs. When I built adh-cli, I made an explicit tradeoff: by routing everything through tightly scoped sub-agents, I was actually consuming more total tokens than a single massive prompt would use. But because my constraint forced me to use a much cheaper model (Gemini Flash), my bet was that the overall system would be far more cost-effective and resilient. AI doesn’t remove the need for architectural judgment; it exponentially increases it. You have to exercise good judgment to know when throwing compute at a problem is a calculated business decision, and when it’s just masking a fragile design.

The Innovation of Constraints

The most interesting work in AI right now isn’t happening where resources are unlimited. It’s happening at the edges, where constraints are severe.

Take local models, for example. When you’re trying to run an LLM on a consumer laptop or a Raspberry Pi, you don’t have the luxury of a 70-billion parameter model. You are forced to use a smaller, quantized model. This constraint forces you to build better architectures. You can’t rely on the model to “know” everything, so you have to optimize at the edge. Maybe you build robust Retrieval-Augmented Generation (RAG) pipelines. Maybe you implement sophisticated memory retrieval systems to surface exactly the right historical context just-in-time. Or maybe you break complex workflows down into tiny, focused sub-agents, each operating with its own tightly constrained context window. You have to craft highly specific, deterministic prompts.

# Instead of one massive prompt, constraints force modularity
def evaluate_code_chunk(chunk: str, context: dict) -> EvaluationResult:
    """
    A tightly scoped function that uses a small, fast local model
    to evaluate a specific piece of code, rather than dumping
    the whole repo into a massive API call.
    """
    prompt = build_focused_prompt(chunk, context)
    response = local_model.generate(prompt, max_tokens=256)
    return parse_evaluation(response)

Just like Jarrett avoiding the tinny upper register, we learn to avoid the weak points of our tools. We build guardrails. We write cleaner code. We design systems that are elegant because they have to be.

Finding Your Broken Piano

Of course, there is a survivorship bias to the Köln Concert. For every broken piano that produces a masterpiece, there are a hundred broken laptops that just result in missed deadlines. Not all constraints are good constraints. You can’t change the laws of physics, and if a structural limitation is genuinely preventing the work from happening, you have to reevaluate. The goal isn’t to suffer for the sake of suffering. But by starting with strict constraints, you force yourself to explore the boundaries. If you prove a task is impossible under those conditions, you can always loosen the constraints and expand your resources. But if you start with infinite resources, you never learn where those boundaries actually are.

Constraints are not the enemy of creativity; they are its prerequisite. Yes, accepting a severe constraint—especially an external one you didn’t choose—can be incredibly painful in the moment. Keith Jarrett hated his broken piano. He didn’t feel freed; he fought against it until he was forced to adapt. But like exercise or eating your vegetables, the value isn’t in the immediate comfort. It’s about the mindset shift. You accept the constraint to build a muscle, to stay fit, to force yourself to find a new path when the easy one is blocked. When we are stripped of our ideal tools and infinite runways, we are forced to abandon our default habits. Whether it’s the self-imposed design rules of adh-cli, the radical minimalism of Mario Zechner’s Pi Agent, or the physical limitations of a broken rehearsal piano in Cologne, constraints force us into a space of deliberate, intentional action.

If you want to build a truly resilient, innovative system, don’t start with the biggest, most expensive tools available. Start with a broken piano. Artificially constrain your resources. Limit your context window. See what you can achieve with a 7B parameter model instead of a flagship API, or see what happens when you strip your agent’s toolkit down to the bare essentials.

You might just find that the limitations force you to build something far better than you would have otherwise—a system that is elegant not in spite of its constraints, but because of them.

So, look around your current projects. Where are you relying on infinite resources to mask lazy architecture? And more importantly: what constraints have you come across in your own work that felt like a frustrating restriction at first, but turned out to be a blessing in disguise? I’d love to hear your stories.