Have Your Claude Call My Claude

A blog post titled AI;DR (AI; Didn’t Read) by Rick Manelius blew up on Hacker News last week. The discussion on HN is amazing, it's like a group therapy session. You can probably guess the premise from the title, but the post is about a problem many of us are having, which is how to respond to people who copy and paste LLM-generated walls of text and expect you, another person, to read it.

I share the author's frustration, and I've been thinking a lot about how to handle it myself lately. If I can tell at a glance that someone copied and pasted Claude's output as something they intend for me to read, it's hard for me to not feel a sudden rush of indignation rising up. 

My policy that I'm trying out is that when I detect a long LLM-generated answer from someone, if it's about a topic that I can't simply ignore, is to fire up my own Claude instance and paste their answer for Claude to summarize for me. There's a pleasant equivalence there that feels right: zero effort met with zero effort.

As Rick mentions in his post, there's an amazing site dontpastetheai.com that feels to me like the bygone-era equivalent of lmgtfy.com. I don't think I quite have the snark in my body to reply to a coworker with AI;DR or a link to a website that mocks what they did, but I do think it's well within bounds to reply to obvious AI slop with your own obvious AI slop.

The productivity benefits of LLMs can't be ignored, but I think we need to remind each other from time to time that we're not just meat pipes connecting two instances of Claude to each other. If that were the case, then why are we here at all?

The Reverse Centaur

I recently watched a couple interviews with Cory Doctorow discussing AI. He explained the concept of the "Reverse Centaur" from the title of his upcoming book.

Here's how he defines that term on his blog:

In automation theory, a “centaur” is a person who is assisted by a machine. You’re a human head being carried around on a tireless robot body. Driving a car makes you a centaur, and so does using autocomplete.

...a reverse centaur is a machine head on a human body, a person who is serving as a squishy meat appendage for an uncaring machine.

...

Obviously, it’s nice to be a centaur, and it’s horrible to be a reverse centaur. There are lots of AI tools that are potentially very centaur-like, but my thesis is that these tools are created and funded for the express purpose of creating reverse-centaurs, which is something none of us want to be.

I think that's such a potent framing, and it matches my experience with GenAI tools like Claude and Copilot. I actually find them pleasant to use when I'm the head of the centaur. There are a lot of tedious little tasks involved in software engineering that I’m happy to outsource to an eager assistant. I maintain my autonomy, intelligence, and dignity in this arrangement.

But I've also encountered advocates in the software industry for the reverse centaur arrangement, where AI agents are autonomously producing all the code, with human engineers essentially grading their work and scolding them. That arrangement I find depressing and alienating. 

In Doctorow's interview with Democracy Now!, he mentions another concept called the "accountability sink", which means the poor human who gets blamed when the AI agents make mistakes that get past the human's red pen.

An often used example of "good AI" is software that can spot hard-to-find tumors on X-rays, even the tumors that a human radiologist missed. Doctorow warns against turning radiologists into accountability sinks:

...if the AI misses a tumor, this will be the human radiologist’s fault, because they are the "human in the loop." It’s their signature on the diagnosis.

The radiologist’s job isn’t really to oversee the AI’s work, it’s to take the blame for the AI’s mistakes.

Over many years of working in the software industry I've seen the pattern of a certain kind of shitty job where the boss wants to hire an accountability sink for a collection of inexpensive offshore developers. A kind of designated responsible party for low quality output.

Imagine if instead of being the accountability sink for contingent workers in a distant timezone, you're now responsible for what the chat bot living in a data center produces. Yikes!

I'll try to hold out being the top end of the centaur for as long as I can.

Dysfunctional In a Chill Way

I've had many jobs over 20+ years in the software industry, and every one of them has been dysfunctional to some degree. Of course, that's normal. No person is without flaws, and the same is true of jobs. Even my best jobs had dysfunction.

There are many ways for a job to be dysfunctional...

  • Micromanagement
  • Unreasonable deadlines
  • Poor communication
  • Etc.

But something I've learned over time is that jobs can be dysfunctional in a miserable way or a chill way.

A company doesn't need to be as dysfunctional as Kruger Industrial Smoothing, but a little dysfunction can be a positive thing for a job...if it's the right kind of dysfunction.

Some of the dysfunctions of a job (e.g., micromanagement) can make one's life stressful. But not always. The chill dysfunctional flip side of micromanagement, little-to-no supervision, can be awesome depending on your situation:

  • Time to work on your own priorities
  • Build according to your own standard of quality
  • Decide what you think is important
  • Relaxed pace

Just like every person, every job has dysfunction. If they're flawed in a chill way, you're doing pretty well.

A Human Writes This Blog

I had to turn off comments on this blog several years ago because most of the comments were being left by bots, and it felt like an uphill battle trying to manually remove them over hundreds of blog posts. I'm really bummed about that, because (back when people still read blogs) I would get a steady trickle of comments from real people who wanted to say something about my posts. It feels more like a void nowadays writing here, but I have just as much to say as I always did, so I keep writing. 

And it saddens me a little bit that blogging is not as common now as it used to be when I started this blog in the late 2000s. It just got too easy to write something on the internet in other ways (Twitter, Facebook, etc.). There has never been more writing on the internet than now, and writing has never been as disposable as it is now.

I've been thinking about putting a note in the sidebar of this blog that says something like, "This blog is never written by AI." Because my personal opinion about GenAI, is that, while there are legitimate uses for it, I believe it's demeaning to expect a human being to seriously read content that an AI has generated. Get another bot to read that shit.

I won't let this blog become part of the dead internet, where bots write for other bots. Hey bots, you're welcome to read this blog, but a human will continue writing it.

Would it be way easier if I just prompted ChatGPT with a blog post idea, watched it spit out text, and then copied and pasted it as a blog post. Hell yes. Would I ever do that? Hell no. Because this blog is about my personal experiences in the software industry, and if I let a bot write it, there's no point to continuing. And I could never feel good about asking another human being to read it.

Sorry readers, you're stuck with me. Enjoy all the rambling, disconnected thoughts, and awkward transitions that only a human can produce. ✌️

Who Wrote This Crap?

I remember many years ago interviewing at a software consultancy where one of the consultants in the meeting was talking about a project they had going on. He was marveling at how fast the team of junior developers on the project was "cranking...out...code." I'll never forget the way he said it, and the look of awe on his face as he slowly shook his head back and forth.

This of course happened well before the advent of LLM assistants that can produce code at an astonishing speed. I wonder if that consultant's head would have literally exploded if I had showed him GitHub Copilot or Claude Code at the time.

Hell, I remember being gobsmacked as a junior engineer myself by CodeSmith, which was an early code generator product for C#. It could automatically spit out huge amounts of boilerplate code that an engineer would typically write by hand. I heard about it from a coworker at my very first job in the software industry, and I remember how unnerved I felt, thinking about a program that can automatically write code (isn't that why I'm here?).

The next code generator that came into my life was Ruby on Rails, which I first tried a couple years later. By that time, I had experienced what it was like to write the most tedious code in most applications, which was code wiring up database tables to web forms, and all the plumbing to pipe the data through each layer. It was a mind-numbing yet essential task, and almost every application needed it. Rails came at me like a breath of fresh air. At this point, I was like, oh yeah, this rules. I hated writing all that data access CRUD, and Rails made it largely disappear. Code generation clicked for me.

In the .NET world, where I've mostly worked for my career, we had a series of object-relational mappers that could generate classes off of your database schema, and resulted in engineers having to write way less code that shuttles data from a web application to a database and back. To name a few, we had NHibernate, SubSonic, LINQ to SQL, and then Entity Framework. I welcomed these tools with open arms, but I do remember there being pushback at the time from more senior people at my companies who were accustomed to writing this kind of code by hand and didn't trust what the tools were doing under the covers.

The common denominator in my early experience with code generation was eliminating the manual work of writing a lot of extremely predictable, relatively dumb, utterly tedious code that was also essential. And this usually took the form of data access code that moved data between layers of a web application, from the front-end to the database, where you had SQL tables that corresponded to C# classes, that corresponded to web forms. Classic CRUD. It's usually highly predictable stuff, and ripe for code generation. Yes, there were times where the tooling would break down, and an engineer would have to get under the covers and debug an edge case that the tool couldn't handle automatically, but, in my experience this was relatively rare.

What I'm seeing in the industry now with AI coding assistants like Copilot feels fundamentally different to me. I've witnessed fellow engineers in recent years generating code more akin to "business logic". This is the kind of code that's specific to the domain of the company and not transferable from codebase-to-codebase. I've also seen fellow engineers letting AI write the code for areas of the codebase that they don't understand well. For example, they may not know how to do a certain thing with React, so they describe what they're trying to do to Copilot, which then generates code that the engineer accepts without understanding, as long as it seems to work.

What's fundamentally different to me about the scenarios I just described and the code generation scenarios of yore, was that the pre-AI code generators were deterministic, and they were applied only to non-domain-specific logic.

What happens when a codebase is peppered with business logic that no human working at the company wrote, and hence cannot definitively explain? I have already seen first-hand times when bugs in important processes were not discovered until the AI-generated code had been in production for weeks. The engineer committing the code did not know that what Copilot generated did not match the logic they intended. Maybe I can write another whole blog post about this topic, but I'll say briefly here that in many scenarios, increasing the speed at which the code is produced is less important than a human understanding what it does at a granular level. In other words, speed of coding is not a bottleneck.

Another consideration is that AI code generators like Copilot are non-deterministic. As in, you can run them multiple times with the same input and they will produce different results. Going back to my examples before of pre-AI tools, the code generation features of CodeSmith and Entity Framework are deterministic. You can run them multiple times with the same input, and they will give you the same output every time. This is because a human software engineer wrote the code behind those tools, and the rules are directly and unambiguously traceable back to the lines of code a human wrote while designing them.

I can't help but wonder if, as an industry, we're hurtling toward a future where many production codebases will be littered with code that no human at the company understands or could definitively explain, not years later, but even weeks later. My personal relationship to AI-generated code as a working software engineer is that I will not commit code that I cannot explain. And when doing code reviews, I cannot accept the explanation that code included in the pull request was AI-generated and hence the submitter does not know what it does.

I also have to wonder if the AI slop era we're all in at the moment says something about the illusory nature of quality. Maybe quality was just an unintended side effect of manual coding that business leaders never really cared much about in the first place. In my multi-decade career in the software industry, the emergence of Copilot represents the first time I've ever experienced non-technical people mandating the use of a particular tool to software engineers. It seems that the idea of faster code production was so mouthwatering that quality flew out the window within seconds.

Who wrote this crap? Maybe the answer never mattered.


Don't Mess With Delivery

If your team has consistent, reliable delivery of working software to production, you’re crushing. Don’t mess with it. It's astonishing how many teams in real world software development can't do this. I've witnessed it over and over and over in my career.

If you have a backlog, sprint planning, a dependable QA function (even if manual and/or slow), and scripted deployments on-demand, you are very fortunate.

Of course, we don't want our engineers working in a feature factory, so we make sure they have input into what they're building and they know why they're building it. That's the motivation to keep delivering consistently. 

So how do teams "mess with" delivery? For teams that already have consistent delivery, the way they tend to mess it up is by prioritizing speed over consistency.

I think that continuous deployment is an incredible technical capability to possess. It's amazing to have a fully automated pipeline capable of taking a code commit and moving it fully out to customers without manual intervention. This is huge for production bugs and emergency fixes.

My belief is that feature work should prioritize ease of communication over raw speed of deployment. Just because it's technically possible to deploy new features every day doesn't mean it's actually beneficial to users. I've seen firsthand the chaos that results when new features appear in front of users without people beyond the immediate team knowing about them.

Every organization wants to "go faster", but I believe once you're touching production, communication matters more than speed. If you're consistently getting new features in front of users every two weeks, you're already doing so well. Optimize something else.

If you've got a reliable, dependable, consistent pipeline of new features to real users on a reasonable sprint duration, it's not worth the communication overhead of going out-of-band. Please don't mess with delivery.

User Advocate vs. Front-End Engineer

I really enjoy doing UI work: being close to the user's mind, thinking deeply about interaction design, and how small changes can massively improve someone's day. But in order to market oneself as a “Front-End Engineer” in the current landscape requires dogged attention to fashion trends, and I'm on Team Evergreen, baby.

Early in my career in web development, I loved reading Jakob Nielsen and Steve Krug, but at some point I had to accept that maintaining intimate awareness of the specifics of JavaScript Framework Du Jour was not sustainable for me. Call me "full-stack", that's fine.

Front-end is the most ripe for résumé-driven engineering. Default skepticism is always warranted for new technologies, but in front-end, it is truly a necessity to avoid lighting your company's money on fire.

I found a rather cathartic post by Marco Rogers on Hacker News recently called The Frontend Treadmill. I fully agree with Marco's take: 

If you are building a product that you hope has longevity, your frontend framework is the least interesting technical decision for you to make. And all of the time you spend arguing about it is wasted energy.

I would not necessarily describe myself as a React fan, but I am very happy that it feels like we finally landed on a framework that can survive for several years, with wide adoption by startups and enterprises alike. Let's go, Lindy effect. Maybe React can be the framework equivalent of what jQuery did as a library. 

Above All Else, Sustainability

From the principles of the Agile Manifesto:

Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.

Extreme measures are always doomed beyond a limited time horizon. Whether we're talking about diets or software development practices, without sustainability you have nothing.

Initiatives to increase productivity, improve quality, lower costs, or any other "good thing" simply don't matter if they're not sustainable.

A common topic in Agile circles is "sustainable pace", which usually focuses on the futility of working overtime, typically over 40 hours a week. I would argue that sustainable pace is about more than just the number of hours clocked in a work day or work week. 

Is the work emotionally sustainable? Do people hate working here? Do people end the work week feeling a sense of accomplishment? Do people feel like their leaders have reasonable expectations? Are they constantly battling reality?

Sometimes the degree to which an enforcer must continuously apply pressure to get people to follow a process indicates how sustainable the process is. Powering through is not a sign of discipline, it’s a sign of delusion.

Ignorance of reality is unsustainable. Coercion is unsustainable. Surveillance is unsustainable.

Can I and the people around me keep this up forever? If not, it's time to pause and reflect. Above all else, sustainability.

Agile as Progressive Enhancement

When I was coming up as a young web developer. the concept of progressive enhancement was kind of at the hipster vanguard of web development. This was in the phase of the web where JavaScript and CSS feature parity amongst browsers could not be taken for granted like it is today.

The idea of progressive enhancement was that you started by making your web application work on an essential informational level with semantic markup, then you layered on presentational and dynamic niceties with CSS and JavaScript respectively, if available to the user agent. At each layer, or each stage, you left the user with something usable and valuable that stood on its own without the other stuff.

Learning about the concept of progressive enhancement early in my career colored my interpretation of the Agile philosophy forever. The iterative nature of Agile methodologies, with "Working software [as] the primary measure of progress" is essential to how I approach making software for users to this day. In my mind's eye, I still visualize iteration as progressive enhancement. We start with the essential core of value, and ship that. If the core of the idea resonates with real users, then we iteratively layer on enhancements to the core, shipping to users at each stage.

In order to work this way, you have to accept that quality and feature completeness are not the same thing. At each layer, we're delivering a scope with high quality always, but with no ambition of completeness. We're delivering shippable increments of software as layers over a core idea of value that we've already shipped. We build out our product backlog as all the nice things we could layer over the core, as we think of them, but they are not essential. One of the amazing things about working this way is that users are delighted by it. The features just keep getting nicer before their eyes.

Legibility

I recently came across a blog post called Seeing Like a Software Company by Sean Goedecke on Hacker News. The post hooked me right away by introducing to my vocabulary the term "legibility": 

By “legible”, I mean work that is predictable, well-estimated, has a paper trail, and doesn’t depend on any contingent factors (like the availability of specific people). Quarterly planning, OKRs, and Jira all exist to make work legible. Illegible work is everything else: asking for and giving favors, using tacit knowledge that isn’t or can’t be written down, fitting in unscheduled changes, and drawing on interpersonal relationships.

One of the advantages that small companies have is that they can achieve greater speed compared to a large company by eschewing legibility. When you know everyone else in the company by name, or maybe you all work in the same room together, you don't need standardized processes to go about your work, in fact, they just slow you down. Work happens through what the author calls illegible backchannels:

An engineer on team A reaches out to an engineer on team B asking “hey, can you make this one-line change for me”. That engineer on team B then does it immediately, maybe creating a ticket, maybe not. Then it’s done! This works great, but it’s illegible because the company can’t expect it or plan for it - it relies on the interpersonal relationships between engineers on different teams, which are very difficult to quantify.

One of the struggles that small companies face as they grow into larger companies is that they can't get by anymore without legibility. Maybe they've grown to hundreds of employees or they're working with people distributed across multiple timezones.

The fact of growing up as a company is that the loss of speed in individual tasks is outweighed by the predictability of process. As the author writes:

The processes that slow engineers down are the same processes that make their work legible to the rest of the company. And that legibility (in dollar terms) is more valuable than being able to produce software more efficiently.

I feel like this is a hard concept to sell people on who are used to working in small companies. In the long run, going slower is actually better for the company. Writing things down and organizing the work in a more structured way reduces the chaos left in the wake of localized, marginal speed-ups.

The author concedes that illegible work is necessary in small doses even in large companies. In cases of show-stopping production bugs, for example, large companies create temporary sanctioned zones of illegibility in which they gather together a strike team of experienced people to swarm on a critical issue until it's resolved. They then return to legibility.

Legibility is a massively useful concept that I will carry with me. It explains so much about how companies function internally, in often counterintuitive ways.

The Financial Architecture of Software

Conway's Law is foundational to software engineering. It says:

Organizations which design systems...are constrained to produce designs which are copies of the communication structures of these organizations.

If you've ever had any job in the software industry, you've almost certainly seen this play out in the architecture of your codebase. Group A works on Thing X and Group B works on Thing Y, so Thing X is in one [project, repository, web service] and Thing Y is in another [project, repository, web service]. You can read the organizational structure in the structure of the technology. It helps to make sense of why things are the way they are. The silos of communication are reflected in the solutions.

But I recently came across this interview on InfoQ with Ian Miell, who I've mentioned on this blog before. He has interesting things to say about the sociology of software development, and in this interview he talks about a book he's writing about the financial architecture of software:

The material aspects of the world ultimately determine the software we build and specifically the decisions we make at a grand scale about the software that we build. 

I remember being frustrated and confused as a junior engineer, not understanding why certain things are prioritized in a company and others aren't. Obvious best practices from the industry at large don't get traction in certain places, what seem like universal good things like "quality" don't seem to matter, or why values spoken about at the company level seem unevenly distributed and applied.

Ian explains...

When I have a code base, I want to make sure all the tabs are correctly aligned before I do anything else that might take time, or I wanted to make sure the naming paths are consistent. But you get larger scale problems where engineers say, "No, we have to fix this now because it's a huge thing". And you say, "Well, I get it, but that's going to take us a month and in that month people are going to be looking at me as the leader of this team and saying, 'What have you delivered?' And I can try and explain to them what I've done, but it's not going to apply".

Hard work is not always rewarded. It depends on who sees the work, what their individual priorities are, and how much money they control. My advice to my younger self would be to pay close attention to what your specific boss thinks is important, and show yourself working on those things. The next level is understanding what your boss's boss thinks is important and by what criteria they're judging your boss; then you'll really understand what kind of work is rewarded.

As Ian describes...

Who owns the budget is a fascinating question. ...I start the book with a story about a very successful engineer, I call her Jan. She becomes a leader of a small group of engineers and she builds a platform in her spare time with those engineers, like a Kubernetes platform. And she does it within a central IT function and she builds it and she takes it, she shows it to her manager and the manager's like, "Well, this doesn't help me hit my goals for the year. This doesn't help me get a promotion, this doesn't help me get a pay rise". And she's nonplussed like. "I'm trying to deliver faster, cheaper, better. That's what the company is, we're supposed to be agile. I'm trying to help the whole business".

And it might sound naive, but a lot of people think this way. I certainly did. Where you're thinking, well, I'm doing what's good for the company, but the system is not structured in that way. It's not designed to be run that way because people are complicated and their interactions are complicated, so you silo things and you have different budgets and so on, and the end result is that you are slaving away at the central IT function, trying to make the business better as a whole, but it's not accounted for.

Understanding where the money comes from sheds light on the technology decisions, where quality matters a lot and where it's almost an afterthought, and why different teams are more scrutinized than others. In the end, follow the money.


Slow Is Smooth, Smooth Is Fast

If you want to move fast, don’t make speed your goal. Make smoothness your goal. Fast naturally follows smooth.

Every software organization wants to be fast. And fast has a certain look to it. Buzzing Slack channels, video calls, people huddled around a screen or whiteboard. It's very easy to conflate "high touch" work practices with speed.

As I mentioned in my previous post, one of the huge benefits of sprint-based development is that sprints act as a delivery protector. A bulwark against constant churn.

The principles of the Agile Manifesto talk repeatedly about change, continuous X, face-to-face conversation. Some teams and leaders really latch onto these concepts. Let's change the requirements every day; let's discuss every decision face-to-face.

One of my favorite principles is:

Working software is the primary measure of progress.

All the churn that looks like "Agility" is canceled out if you don't deliver on a regular enough cadence that your customers notice. They don't see anything else, they don't measure you by anything else.

The passé methodology of Waterfall became passé because it didn't respond to change well enough. People were wed to process instead of reality. Why spend a year building something that no one actually wants, right?

But I firmly believe there is such a thing as being too responsive to change. There is such a thing as too much communication. Remember, your customers only see delivery. They only see what pops out the other end.

There is a benefit to following a well-defined process from idea to delivery. Process is perhaps a dirty word in Agile circles, but I think in some ways being anti-process is being anti-delivery. Every time we circumvent a process, we create communication overhead. Each team member impacted must be notified and followed up with to make sure they know about the circumvention. 

Any change is only good if the ripple effects created by the change are worth suffering. I reject the interpretation that Agile implies that more change is better. We have to always balance change with delivery. In fact, we have to balance anything that takes time and effort for a software team with delivery.

Sprints Balance Consistent Delivery With Optimal Value

Value, value, getting value to users. Work on the most valuable thing, always. You could probably summarize the Agile philosophy as "deliver value to end users faster." What's the next most valuable thing we could work on, get it done, and get it out to production.

With a sprint cadence, we commit to a strategy for two weeks, we work as a team through the items in our sprint backlog, and we retrospect at the end to talk about how we did. There's always the chance that some circumstance around the business could change mid-sprint that may change the team's idea about what is most valuable to work on in the moment. Since sprints balance consistent delivery with optimal value, we keep our heads down and finish the chunk of work we agreed to deliver at the start of the sprint.

This requires leaders and engineers to get used to telling people outside the team, "That's a great idea! We'll put it in the product backlog. We can start working on implementing this idea in less than 2 weeks from today!"

As arbitrary as the sprint cadence can seem, I see the sprint as a delivery protector--a built-in reason to say not right now. The items we're working on today may only be 80% of optimal value, but we're going to finish them dammit. In no more than two weeks we can shift our strategy 180 degrees if necessary, but not today.

Sprints Need a Cooldown

Teams vary in the amount of handwringing their leaders do about sprints that aren't perfectly rightsized. What if we bit off too much work at the start of the sprint and can't finish it all by the end? What if we overestimated the work and didn't fit in as many tickets as we could have?

I've worked with teams where having work incomplete on the last day of the sprint was no big deal, it happened regularly, and no one was too concerned about it. And I've worked with teams where the team committing to a certain scope of work was sacred, and tickets carrying over was a grave error that we fretted over and vowed not to repeat.

The teams that prefer the overstuffed sprint are trying to maximize velocity and reduce downtime of the team members. Teams that treat the sprint timebox as sacred are trying to maximize predictability and often striving for a kind of "standardization" across teams. Some teams will even wonder why they're bothering with these stupid sprint things and opt out for a methodology like Kanban where people just grab work items when they finish the previous one, and toss out the timebox.

I have always believed strongly that whether you're using Scrum or Kanban, and working in discrete timeboxes or not, that team members need a regular cooldown. Just as an athlete cannot sprint indefinitely and must rest, I believe the same is true of software engineering teams.

As you might imagine, this puts me in the camp of "fill the sprint conservatively so that we don't go over." This, of course, means that many sprints will end without the theoretical maximum amount of trackable work items packed into them. And to that I say: Good. That's the point.

I believe that implicit in the social contract of teams is this: If you want people to sprint, then they must be allowed to cool down. That's the deal. Continuous sprinting means burnout. 

Quoting a previous post of mine, here are a few things engineers need to do that are not represented by tickets in a sprint backlog, and are perfect for filling the cooldown period at the end of a sprint, at their discretion:

  • Training
  • Preparing for a presentation
  • Proof of concept / demo of an intriguing tool, framework, technology
  • Updating documentation that's been bugging you
  • Spikes into performance improvements
  • Cleaning out your inbox
  • For companies that do some sort of periodic "goal setting" for each employee, let people work on goals from their list

If we set a strong sprint goal, bite off a chunk of work that the team is confident of completing, and we get it all done without rushing a day or two before the end of our sprint, that's a good thing. The team feels a sense of accomplishment, the business gets a valuable increment of working software that fulfills a real need, and the individuals get some time to cool down and shift their attention to some low-intensity stuff that nevertheless needs doing before the next sprint starts again.

For me, the cooldown at the end of the sprint is what sustainable pace is all about. We go hard toward an important goal, and if our planning and teamwork are on point, then we know we'll have that down-shift at the end.

There's a Fine Line Between Increasing Productivity and De-skilling

In the software engineering profession, there's a concept called "resumeware". It's software that was made with the engineers involved in the design specifically making choices about technology stacks in order to have marketable experience to put on their resume in order to improve their future job prospects, regardless of whether they're a reasonable choice for the system at hand. You can kind of tell when you're looking at resumeware because it looks like the architects threw in every fashionable programming language, library, framework, and architectural pattern into an incoherent hodgepodge.

You could say this is a cynical take on the employer-employee relationship, and perhaps, taking advantage of one's aura of expertise. And that could be true. There's a balance to be had in any job between giving your employer a good return on your salary and the desire of an at-will employee to increase their transferable skills. I think every employer knows this--in fact the good ones use this as a recruitment advantage.

The flip side of this coin is de-skilling, the centuries old drive by employers to reduce the skill needed to produce a product, as part of the endless push to reduce labor costs. Today generative AI is hailed as the ultimate de-skilling force. Employees across all kinds of industries (including software engineering) are feeling the heat from their bosses to increase their use of GenAI in their everyday work. I think that history shows plainly that the march of capital toward greater efficiency is unstoppable. Smashing looms is not a sustainable defense.

When I look into my crystal ball, I see a "Great Sort" coming to the software industry. Just as the middle class of well...life...continues to shrink, I can see the same thing coming to the skilled profession of the software engineer. The question is: Do you want to be sorted to the bottom or the top? Do you want your work to become more skilled or less skilled?

I really do feel for junior software engineers coming out of college in 2025, the time of your career when you're the least skilled, and trying to find a great job while you're trying to get your foot in the door in the age of GenAI. The least skilled are the most vulnerable.

For those among us fortunate enough to be past our junior years to the "experienced" stage of our careers already, what does the future hold? My personal perspective is that I, as I always have, fully embrace the idea of reducing the tedious aspects of my daily work. Programming can be bloody annoying a lot of the time. Assembly language is not a skill I want to have because C# exists, and it's way less annoying to use. There is no way in hell that I'm going to write code in Notepad with a command-line compiler (like I did in college) when IntelliSense and Visual Studio exist. Remember trying to track down the cause of weird error messages in your program before Stack Overflow existed? I do! And it fucking sucked.

I will never take the stance that I want to stay inefficient. I do not want to do my work in a more tedious, annoying way so that I can bill more hours to do the work. But at the same time, I will not de-skill myself for anyone. My general feeling about the big, scary "AI" future of software engineering is that I'm happy to use any tool that makes my job less annoying. My real skill is in producing great software. I am not attached to the tools that I use to do it today or tomorrow. If a statistical word predictor trained on the entirety of Stack Overflow, and fully integrated into my code editor, saves me having to read endless Stack Overflow posts in dozens of tabs in my web browser to produce the same end result, then bring it on.

My personal rubric for productivity-enhancing tools is this: Is my work getting less annoying or am I getting dumber? I refuse to get dumber. Getting dumber is the feeling of sorting to the bottom. If a statistical word predictor can do my job better than me, then I need to learn to do something harder. I will not try to compete at the level of the tool. I will be sorted up, not down.

Just as any employer has the natural right to decrease their cost to make a product, I retain the right to increase my transferable skills. There's a thin line between increasing productivity and de-skilling. I'm all for increasing productivity, but I will not de-skill myself. 

Calibrating Your Care

I've been a subscriber to the r/ExperiencedDevs subreddit for a few months, and I've noticed a common pattern.

Someone will post a question to the group desperately seeking advice about some stressful situation that they are clearly very worked up about at their job. The author's specific concern or complaint could be just about anything, but the general feeling is that from their point of view everything seems to be on fire around them and none of their peers or managers are doing anything about it.

Being around social media for many years trains one to expect posts like these to be opportunities for commenters to rage about the injustice on display and to pile on with their own indignation about incompetent managers and clueless coworkers.

Instead, many of the highest voted comments to the post will be something to the effect of:

With all due respect, I think you care too much about this.

Or...

You seem to be taking this personally, and there's no need to do that.

I was definitely surprised at first by responses like this being highly upvoted, but I just kept seeing them from different people on different posts, with very little pushback from other commenters.

Early in my career, I took things at work very personally. I saw it as a badge of honor to care more than the people around you. I should be mad if things are not being done the right way.

The unpalatable truth, in many cases, is that this thing that seems of grave importance to you...actually doesn't matter as much as you think it does. And the clueless coworkers and managers around you actually understand something important that you don't. In fact, you might be the clueless one.

It's easy to get caught in the spotlight effect, where you assume your thoughts and feelings about a situation are the right ones. And it's easy to believe that your view of what's important is shared by everyone.

If your ground level belief is that everything is on fire and no one cares, you’re probably right. But you have to keep in mind that no one else sees the world through your eyes. You may be reading the room inaccurately. Your boss may have more context around the project than your daily experience.

One could take the advice that "You care too much" as being told "You should be dead inside." I would say, just be prepared to lower the stakes in your mind. Be humble about your own role in the world of people around you. Get some mental distance from the situation and try to read the room.

Everybody gets to decide what they care about. Calibrate your care.

Don't Let an LLM Write Your Unit Tests

Writing unit tests is one of those tasks that software engineers often see as tedious. In fact, I've heard from fellow engineers that unit test generation is one of their favorite uses of LLMs. Let the AI write them! I've always been deeply uncomfortable with this use of LLMs, even though I'm all for reducing the tedious aspects of work in general.

Unit tests are not boilerplate code. If you've read a lot of bad unit tests, then you may get that impression, though. If your only goal in writing unit tests is Coverage number goes up!” then by all means let an LLM write your unit tests.

But I would argue that writing great unit tests means thinking carefully about what is valuable about the system under test. An LLM is great at generating reams of code in an instant, but it will never know anything about the value of the product your codebase represents.

If our goal instead is “Let’s ensure that our product is retaining its value as we add more code to our codebase.” then a human who understands the value of the product should write the tests.

Unit tests have yet another critical function, which is to provide runnable documentation about the system for other engineers to read. Want to know how some part of the codebase is supposed to work? Go read the unit tests; they represent the valuable features of the software that an engineer wanted to point out.

Code is written once, but read many times. And test coverage is just a number. If you want tests that matter, write them yourself.

Copilot Is Outsourcing 2.0

Decision-makers can remain irrational longer than you can remain solvent. (Or, in this context, remain employed.)

- Will Larson, "Career Advice in 2025"

"AI" coding tools like Copilot went from a mere curiosity to full-blown silver bullet territory in a few short years. I'm all for reducing the accidental complexity Fred Brooks wrote about in his classic essay on software engineering, but what’s driving me a little bit crazy lately is talk amongst people (mostly non-engineers) around the industry that Copilot and the like are magical productivity machines.

I'm not the only seasoned engineer to notice that this present moment feels eerily similar to the outsourcing craze of the 2000s. The dream of every non-technical executive who had a bunch of software engineers on their payroll was to lay them off and replace them with people in faraway countries with a lower standard of living who they could pay less to do the same job. Well...it didn't exactly work out that way. It turns out that software engineers are not, in fact, fungible code-typing machines. Outsourcing jobs to cheaper parts of the world is very much still a thing, but it took several years of reality colliding with the dream to bring expectations down to a reasonable level.

In 2025, the executive's dream is back, and bigger than ever. What's better than paying humans in other parts of the planet less to do the same job? We can get AI™ to do the job for even less!

The outsized hype around AI coding tools is premised on the idea that software engineers are primarily code-writers. If we could get machines to type the code, then we can save a ton of money, right? The reality in my experience is that typing code faster is not a bottleneck for any software engineer that you would want working on your product. Writing code is a small percentage of what a software engineer actually does.

I remember a friend of mine from college asking me many years ago, when learning that I was studying Computer Science, if I was worried about destroying my hands from typing so much. He apparently imagined that the career I was training for was similar to a court stenographer. In the software industry, we used to refer to engineers who produced huge volumes of repetitive code quickly as "code monkeys". This matches the outside perception of someone furiously hacking away at a keyboard, with speed of keystrokes making the overall impression.

The thing is, a “code monkey” is replaceable by AI. If there's anything we've learned over the last 200 years or so, it's that machines can do repetitive, mindless tasks faster and more cheaply than humans. If the work of a software engineer really looked like: 1. Do a Google search. 2. Copy and paste code from your web browser into your code editor. 3. Hit a button to commit to your team's code repository; then heck yeah, a machine can do those things way faster than a human.

I don't fear being replaced by an AI that can copy and paste code from the internet faster than I can. I do worry about non-technical executives either firing me or not hiring me based on marketing, or a belief that this is what software engineers primarily do to create software that is valuable to an actual business.

Just like the executive's dream of outsourcing faded to a modest incremental reduction in the cost of developing software, I believe that so to will the dream of the AI engineer. I don't need to ask Fred Brooks what he would have thought (RIP).

Engineering-led Research Tickets

One of my optimistic beliefs about software engineers is that we're full of good ideas about how things around us could be improved. We're smart people, problem solvers. We read tech news, we're friends with engineers at other companies, we bring diverse career experiences to each job--in other words, we're at least vaguely aware of better ways of doing things that are happening in other places.

If we want to go beyond just talking about better ways of doing things, we need to empower engineers to capture their ideas for improvement in the backlog right next to ideas from the product management side. It's critical, of course, that items in the backlog generated by engineers are refined, estimated, and actually included in sprints on a regular basis.

And we want to go beyond the all-too-easy step of adding a one-line ticket at the end of our backlog that says “do cool thing X”. Since no one has the foggiest idea how we’ll actually do that cool thing, the backlog item just sits there and no one ever puts it into a sprint because it’s too nebulous and scary and we don’t know how long it will take or if it’s even possible in a sprint-worth’s amount of time.

Make that “do cool thing X” a research ticket. Time-box the effort. We can put this into a future sprint because at the end of the time box (fitting within one sprint) we will have a documented outcome that gets us closer to the goal than we were before, and we’ll have a much better idea of the actual steps to get there with "real" tickets representing the work.

The output of these research tickets can be a comment saying we shouldn’t do this and here’s why. Or the output could be a series of other tickets the engineer creates with a ticket for each step of the process toward accomplishing the ultimate goal...or a nice intermediate goal.

Even if we end up looking at the list of steps for follow-on work and think, “Geez! That’s a lot of damn work to get the outcome we want!” and we decide it’s not worth the effort, that's still a great outcome.


Software Engineer Career Levels at Companies You've Heard Of

I found a site called Progression.fyi that curates a list of "career frameworks," or in other words, the level-progression system of titles that a bunch of tech companies use for their software engineers. It's pretty interesting to peruse.

Several of these companies define what the often nebulous title of Staff Engineer means to them, which I find particularly compelling.

Here are a few companies you've probably heard of, with links to their pages where they define their career progressions for software engineers: