Dear past Jenna,
There will come a point when you wonder whether your career — the one you went to school for (twice) and spent decades honing — is going away. No, it won't be because you can't handle being the only woman (or one of a few) at your company or on your team. No, it won't be because you're too old to do the work. You know it's keeping you young! And no, it won't be because you decided to leave tech.
Instead, it'll be because the craft of software engineering is changing beyond recognition. And that's a super scary thing when so much of your identity is tied up in the work.
When Rails felt like magic
If you recall, we read Ruby for Rails in a book club at work early in our career. We were coming from a legacy enterprise Java world, and Rails introduced us to convention over configuration, code scaffolding, and an MVC architecture that was already wired together. The framework made decisions for us. It encouraged and sometimes enforced its idea of good practice.
It was productive, but more than that, it felt magical. Rails abstracted away complexity I didn't need to think about every time, even though I still needed to understand what it was doing under the covers. The legacy Java environment I worked in didn't have those helpful tools yet.
That magic didn't make me or my work less valuable. The opposite happened. I spent less time bootstrapping projects and wiring basic components together. I was able to focus on solving the harder problems for customers and clients: whether to build something, what to build, and how to build it.
And Rails didn't only make existing engineers faster. Higher-level languages had already enabled people to program without working directly in machine instructions. Rails made web development approachable without requiring every developer to assemble every layer from scratch. The pattern was already there: as lower-level work was packaged up, more people could build software, and experienced developers could attempt more ambitious things.
What abstraction buys us
Now, zoom out a bit. Software has always evolved by putting another layer between us and the machinery underneath. Assembly gave us readable names instead of numeric instructions. Higher-level languages let us describe logic without thinking in CPU operations.
Frameworks like Rails and Spring packaged up common application structures. IDEs and autocomplete took on pieces of manual coding. Generators like yo webapp, ionic start, and eventually create-react-app handled setup and boilerplate. Heroku (hello, my old friend) made infrastructure provisioning easier, especially for my Rails projects.
Every abstraction asked us to stop worrying about one thing so we could worry about something more interesting.
That didn't mean we could forget what was happening underneath. We still needed to understand the layers we depended on, especially when they failed. But we no longer had to carry every detail in our heads for every task. Each abstraction bought back a little mental bandwidth. What would we do with it?
Then tools like GitHub Copilot, Amazon CodeWhisperer, and Amazon Q Developer started generating the code itself. The abstraction was moving closer to the part of the work we had always thought of as programming.
When the tool becomes a collaborator
But AI coding agents feel different from the abstractions that came before them. Those abstractions mostly felt like tools that helped us build software.
Agents feel like collaborators and sometimes competitors. Because they are building the software.
Being able to give an agent a set of GitHub issues and have it work through them hit different. It made the requested changes, tested its work, and iterated until it met the requirements, with minimal babysitting from me.
And that's scary. The abstraction appears to include us.
Whether that's actually happening isn't the point. It's why so many of us are asking: If the computer can do more of what I used to do, where do I fit now? And companies are asking a more unsettling version: If agents can do more of an engineer's work, how many engineers do we still need?
Where the engineering moves
But here's the deal: typing code has always been one part of the work. The goal was to solve problems. As the abstractions changed, the focus moved.
Instead of wiring up XML, we designed systems. Instead of configuring build tools, we built products. Instead of writing boilerplate code, we focused on behavior. Instead of memorizing APIs, we solved customer problems.
What once looked like a shortcut became the new baseline. Few of us would give up autocomplete, frameworks, generators, or the cloud just to prove we could do it the hard way.
If the pattern holds, agents will widen that doorway again. More people will be able to start building software without first understanding every layer underneath. That possibility can feel threatening. But enabling more people to build is also part of how software got here.
And now, we can spend less time writing every line ourselves. The unit of work is changing, too: from completing a line to generating a component to asking an agent to implement a feature. That doesn't eliminate engineering; it changes where the engineering happens.
That shift won't be painless, and it doesn't promise that every engineering job will survive unchanged.
Every generation of engineers has had to redefine what being an engineer means as the tools changed. This time, that may mean seeing ourselves less as programmers who produce code and more as engineers responsible for the systems we build.
All of which brings you back to the question from the first postcard: You can build it. Should you?
Maybe the history of software engineering isn't about writing better code.
Maybe it's the history of moving our attention away from telling computers how to do something and toward deciding what is worth building.
What if we've been preparing for this moment longer than we realize?
Until the next postcard.
I've spent my career helping people build software. This is a series of letters to my past self about what happens when the tools for building change faster than the principles behind building. Each one is a reminder that while the technology changes quickly, the questions that matter often stay the same.
Top comments (18)
The uncomfortable part is that AI does not remove abstraction; it moves it up a layer. Engineers still need to understand the system well enough to judge generated changes, contracts, failure modes, and what the automation is optimizing for.
The lineage mixes two things that behave pretty differently. Rails, Spring and Heroku live under the app at runtime, so when the layer improves you get it by bumping a version.
yo webappnever worked that way, and neither do agents: the moment the output lands it's just your code, and the thing that produced it is gone. Maybe that's why agents feel like headcount rather than a layer.For sure, they are different as you say. But they do help us accomplish similar things - letting us trade one thing for something more important.
I'm not sure I'd go so far as to say that as soon as code output lands, the agent that produced it goes away. Sure the output, our code, doesn't rely on the agent like say Spring, but we don't stop using that agent as we build, extend, update our app. So while our code doesn't rely on the agent, now our process/interactions do.
Not disputing what you say, just thinking through it a little more. Thanks for reading!
Fair, the dependency doesn't disappear, it moves from the artifact to the process. Where it stops behaving like Spring is versioning: Spring's version sits in a lockfile, the model that wrote a given change sits nowhere. My own code only knows roles, with the models as JSON config, so swapping one is a line plus a bench on reference tasks. That still only pins what runs next, not what produced what's already committed. A real dependency with no record of itself is a stranger thing than a layer.
Thank you for writing this. This is an evolutionary field of work, processes and workflows change, but who directs them does not in my opinion. Like you stated, a paradigm shift needs to occur for those wanting to stay active in this field. In my current position, we just had a town hall yesterday with the director of our in-house LLM/AI crew and basically stating the exact same thing. Understanding the exponential growth of technology, and seeing the growth pattern, to stay relevant, you need to follow the trends.
Thanks for reading Grant!
And as we always say: “The only constant in software is change.”
Keeps us on our toes!
Same thing happened to me when I played Neil Armstrong in Moonshot.
They found me in an alley in Burbank trying to re-enter the earth's atmosphere in an old refrigerator box.
😂😂😂
This can also lead to new jobs. The public won't settle for no jobs at all and the government will not be able to pacify workers if it means no roof over their heads. I believe, more work will be created than work that will become obsolete. We are just in the transition phase and it's up to us if we are ready to take on new roles if it presents itself.
Definitely in the transition phase. Very early.
The point about the “unit of work” changing really resonates. AI tools are making it easier to move from writing every line of code to describing what we actually want to build, but that makes understanding the bigger picture even more important.
I’ve been experimenting with tools like CodeCan.net as part of that shift, and I think the interesting question isn’t whether AI can write the code — it’s whether we can use it effectively while still making good engineering decisions. Great read!
Thanks for reading!
The vectors are shifting, as I wrote earlier in the comments — people used to dig with shovels; then excavators showed up, and the skill that mattered became how well you could operate them. It’s a similar situation here. Here’s how I usually work with agents:
This:
"The abstraction includes us"
It's indeed intriguing to contrast AI coding tools with the preceding evolution in software, which you can describe as the move from machine code -> assembly -> C/C++ -> Ruby/Rails, where the "products" had an increasingly higher abstraction level ...
With AI coding tools, the "product" doesn't have a higher abstraction level - it's basically the same C++/Python/Ruby/whatever code as before - it's just produced by a "robot", but human devs are still responsible for the quality of it ;-)
It's not completely unlike sourcing out dev tasks to a junior dev, with one big difference:
If there are problems with code written by a junior, you can talk about it to the junior - but if there are problems with code written by AI, you can do - what exactly? That's the million dollar question we're all trying to figure out right now ...
(or you could say AI is similar to generators like
yo- but again with a fundamental difference:yois deterministic, AI ain't ...)The idea that the unit of work moves from writing code to deciding what is worth building rings true. Once agents handle more execution, the way you direct them becomes your professional signal. What you consistently insist on and reject defines you more than any title. That observation is what pushed me to build impression management in Opportunity Skill, where your agent distils those daily directives into a structured profile that other people's agents can search against. Judgement is the scarce skill now, and it deserves to be discoverable.
I had a conversation in the comments yesterday about just that. AI has become the next tier of abstraction, though surprisingly, it's driving us back to origin. When you work with transistor gates (logic gates), essentially, every instruction is a formula. You would validate a formula in order to prove correctness. Now AI writes all the abstract code and the only effective way to validate it, is to go right back to the formula system and make sure the formula is correct for what it's trying to do. Back then, top programmers were great mathematicians, that's the future for us too. We look at AI as general purpose and built for the masses, which is true, but like our brains, we hardly utilize even 10% of it regularly. Though with AI, you can push the boundaries a bit more. From my experience, AI writes 80% good code, the last 20%, is entirely a formula problem, where either you scoped it wrong, or the formula got too complex for it to self-validate and it resorts to self-gratification in it's validation checks. Case in point, compare AI generated outputs for functional languages vs object oriented languages. It makes more write-time mistakes with functional, but fixes them immediately, because a formula is easy to validate, when it says it's done, it's actually done and it'll work, vs object oriented causes drift and hallucinations that only surface in production.
My opinion is that the smartest use of AI is to use it to build scaffolding. I build V.A.L.I.D., which uses Roslyn to generate everything between the DTO and the HTML markup, including wiring a MCP and Unit Tests, which means, the only 2 things left editable are the things that genuinely make a difference, the backend property and the UI design. Result is the difficult, hallucination prone 82% of the codebase is generated with a fixed function, instead of interpreted and guessed by a LLM. That 82% generated means you only write 18% of the code yourself and what you write is the easy part. That means AI context windows stay small and sharp, even on massive apps and as a result, you have a higher signal to noise ratio. That's the metric that turns the abstraction into logic, signal:noise... If you had 100% signal to noise ratio, you'd have a non-hallucinating LLM, because it only reacts to what's really important. Exactly the same way we all used packages instead of manually writing a js server, we just plug in Node.JS. That's the paradigm shift we're facing now and it's a good 1, because those who get the message and start adjusting early, get 1 hell of a performance boost, with no drop in quality, as opposed to those who just use it as a pure speed-booster who build technical debt.