Two Years of AI Changed 20 Years of Habits
Contents
This article is my contribution to Pragmatic Engineer’s essay challenge about how AI changed the way I work in the context of my work at Atlassian.
My career as a software engineer started in 2007, when I interviewed for a software engineer role at a software consultancy in Germany, fresh from university. The interviewer asked me if I knew Java, Hibernate, J2EE, and EJB. I answered “yes” to Java (because I had theoretical knowledge from uni) and “no” to the others. My interviewer said to come back in two months once I had learned these technologies. I did, and apparently had learned enough in that time to navigate the second interview well enough to get the role. I stayed with this company for 12 years. Each client project brought new technologies I had to learn.
In 2019, I moved overseas to start a job at Atlassian in Australia. I was glad that the interviewers did not ask me about EJB this time. In fact they didn’t ask me any technology-related questions and instead focused only on coding and design skills in any language. The expectation that I would be able to pick up new things was implied. And it would be necessary: I was inundated with technologies I had never even thought about before starting this job.
I’m telling you about my experiences of getting into new jobs because they are a showcase for the software industry: you have to keep learning new technologies to get ahead in your career. Often, you have to keep learning the new stuff just to even stay where you are. In the case of LLMs and their use in software engineering, I even dare say that you have to learn them to avoid being utterly left behind. If you take only a single thing from this article, make it that. Don’t panic, take your time, but experiment with AI until you are comfortable with it.
I’ve been thinking about AI and how it affects my work a lot in the last year or so, as I’m sure everyone in the software industry has. I think I have successfully overcome the identity crisis that came with the rise of the frontier models earlier this year, since I am no longer worried that I have to find an alternative career. I still have some unresolved issues with AI, but I am excited about the possibilities that AI brings to our industry. Here, I’m going to walk through the evolution of my relationship with AI over the last two years, in the context of my work as a Principal Engineer at Atlassian.
The early days (2024)
It is a bit strange to think of 2024 as “the early days” of AI, given only two years have passed since then. ChatGPT had been around for roughly two years already, but I hadn’t taken AI seriously yet.
I was in what could be considered a “developer productivity” team back then. We were responsible for the backend tech stacks at Atlassian. There were (and still are) a handful of different tech stacks in use across hundreds of dev teams. Most teams use Java/Kotlin, but there is wide use of Node.js, too. In cooperation with the tech stack communities in the company, we came up with a standard for each tech stack, which listed recommendations about what to do and what not to do when working with a given stack. The most important recommendations were about what libraries to use and not to use. There was a lot of tribal knowledge about certain libraries that caused production incidents because of vulnerabilities or incompatibilities, so we wanted to make sure that teams would know which libraries to use for which purpose and why. And, in general, we thought it a good idea to reduce the sprawl of dependencies across the company to make knowledge transferable between teams and keep things cheaper to maintain.
Atlassian was already investing in AI at that time, starting to build out the “Rovo” brand—our AI product built on top of LLMs. I didn’t think of it as much more than a chatbot back then. It was introduced in some product surfaces, most prominently in Confluence, where it felt to me about as helpful as Clippy in Microsoft Word (spoiler: that changed dramatically once the AI models became good and as the Rovo harness gained more and more context over time). The first interesting use case (for me, at least) I came across was the Rovo integration in Jira automations that became available late in 2024. With automations, you can react to certain events in Jira (and other Atlassian products) and then trigger certain actions. One of these actions was now a Rovo agent, which you could configure with a prompt that can react to input from a previous action or event.
On my team, we thought about how to use this to further our mission, which was to make it as easy as possible for our dev teams to “do the right thing”, which for us mainly meant using the right library (and version of the library) for a given job. There was already a system in place that crawled through all code repositories and ran a bunch of scans on them. Some of those scans were ours, and they checked if certain problematic libraries were being used. For each violation this system found, it would raise a Jira issue. And you know what happens with Jira issues that someone else creates for you … yeah, they get ignored (I understand the irony of writing this as an Atlassian employee).
So, instead of “delivering Jira issues” to our dev teams for them to ignore, we wanted to “deliver outcomes”. In our case, the outcome was a pull request that made the code changes necessary to migrate the codebase to the alternative, desired library. An example of this might be the migration from one major version to the next, which often involves more than just bumping the version number. We had hundreds of codebases that still used major versions that had been outdated for years … the usual tech debt that never gets paid, but may eventually lead to security incidents or time-consuming migrations. We created a prototype that used the new Rovo automation in Jira for just this. The scanning system would still create a Jira issue. This would trigger an automation rule that sends it to a Rovo action. The Rovo action would try to understand which of a handful of migrations would fix the violation reported by the Jira issue. Based on this classification, the automation would trigger another Rovo action which would clone the repository, run a given “codemod” on it (i.e. a program that executes a set of rules that modify a codebase deterministically), and raise a pull request with the result. Rovo did a simple classification and then executed some code we prepared for it. Easy.
I demoed this prototype at Atlassian’s engineering conference called EngFest in late 2024. And during this demo, the AI hallucinated and the classification failed. Just when I was about to get excited about AI, it failed. We didn’t continue working on the automation prototype because we couldn’t get it to work reliably and were back to delivering Jira issues instead of outcomes to our dev teams.
AI had also arrived in the larger developer community at Atlassian. We were given the choice between Cursor and GitHub Copilot for our daily work. I opted for Copilot because I didn’t want to part with my trusted IntelliJ IDE yet. I remember turning Copilot off when it had annoyed me one too many times by giving me useless auto-completion suggestions.
Outside of my job, I had also started using ChatGPT for translations. I had just written a book in German and wanted to translate some chapters into English to publish them separately. So I pasted the English manuscript into ChatGPT chapter by chapter and asked it to translate. It worked okay-ish, but I remember a couple of times when the translation suddenly stopped being a translation of the original and branched off into something that was roughly on topic but had nothing to do with my original text. Meh.
The calm before the storm (2025)
Throughout 2025, I continued to be not very impressed by AI. My team got re-orged and I found myself in an org with the mission to apply codemods at scale across Atlassian codebases. That’s an interesting problem to work on, but my recent experience using AI for applying codemods had spoiled my enthusiasm for this a bit. Also, the new org was more focused on Atlassian’s frontend code and left the thousands of backend code repositories at Atlassian out of scope, which was painful for me as a backend engineer, so I eventually decided to move teams.
Not much changed in my personal use of AI through the year. I was aware of increasing investment into AI features at Atlassian and company-level goals to make Atlassian an “AI first” company, but I took a step back from the front row and let others bang their heads against hallucinating LLMs. The new team I joined was focused on backup and recovery of the datastores backing Atlassian’s central data platform, so we were very conservative and wouldn’t even think about letting the current wild-west AI models touch our code.
It’s getting windy (early 2026)
In early 2026 things started to get exciting again. LLMs were actually starting to become helpful in everyday software engineering tasks. Rovo had made it into my IntelliJ IDE as a plugin I now used daily to interact with the code I was working on. I would ask it questions about the code and it would usually give me some value for my money (well, my employer’s money). My mode of working with code slowly shifted from only manual changes to manual changes informed by AI to AI-made changes heavily edited by me.
I eventually switched to RovoCLI as my AI tooling of choice, because I didn’t like the UX of an IDE plugin. I was also starting to let AI make code changes more autonomously without micro-managing, and it had earned a promotion to a standalone tool. I also enjoy working in a terminal in general, so RovoCLI was the closest choice. It let me choose the underlying model including all the frontier models. RovoCLI is inspired by tools like the Claude Code CLI, with default MCP integrations to all Atlassian products, which we use heavily in our day-to-day work. Underlying the MCP integration is the Teamwork Graph CLI, which gives an LLM a bunch of commands it can use to interact with Atlassian products and the “graph” of my data within those products. I realized that this is really powerful when I started to interact with Bitbucket pull requests, Jira issues, and Confluence pages via AI daily. I could just point it at a Jira issue or Confluence page, give it some additional context, and ask it to work on it. What I took away from this is that if you want to make your product easily accessible for AI agents, create a CLI and some skills around it—this gives AI agents superpowers, even without an MCP integration.
Around this time, I heard about AutoDev, a product that was developed at Atlassian based on the Rovo infrastructure with the goal to automate the software development cycle. The idea was that you work on your backlog in Jira and AutoDev would automatically pick up work items from the backlog and raise pull requests, then react to build failures and pull request comments and so on by modifying the code, just like a human developer would. This sounded like the evolution of my failed experiment with automations from two years earlier. By now, though, I was convinced that AI agents can actually do meaningful things with code so I signed my team up for an alpha test. The system instantly scanned our backlog and raised a bunch of pull requests in our repositories. We never ended up reviewing them and eventually discarded them. The pull requests didn’t work for us because the quality of the descriptions in our Jira issues was not good enough for an AI agent to do the right thing. Also, it picked up backlog items that were just not ready to work on yet. In one case, it raised a pull request in a repo from another team where it reverted a change we previously made, because we had a “cleanup” ticket in the backlog so we wouldn’t forget to clean up after a migration was finished. But the migration wasn’t finished yet, and this revert would have caused a real incident had it been merged too early. So, we asked for more control over which backlog items AutoDev would pick up and we got a new feature: we could tag the backlog items we wanted it to pick up.
It turns out, however, that we never tag backlog items for AutoDev to pick up, because we don’t (want to) spend enough time polishing Jira issue descriptions in our refinement sessions to add all the context an AI agent would need. We usually do not even know all the context at the time of refinement and are happy to leave task descriptions a bit rougher and leave it to the engineer who is picking up this task to work it out. We usually build the context while working on a task. I suspect we’re also resisting the idea a bit because we just enjoy “pairing” with an AI agent on a problem more than just specifying the problem and then waiting until the AI agent has raised a pull request to review it. As I mentioned to a PM on the AutoDev team: it’s not a technological problem that we’re not letting AI agents work on our backlog autonomously, it’s a culture problem. My team might just not be ready for it yet.
Early 2026 was also the time when tokenmaxxing became a thing. We had unlimited tokens and token usage became a “data point” for each engineer and—of course—someone built a scoreboard on top of that data so we could compare. That scoreboard was taken down again after a month or so, though, for privacy reasons, and replaced with a dashboard where you can see just your own data. It’s good to know how much of the company’s money I spend on tokens so I can try to be more efficient. But I also know that data like this can drive some strange behavior of which tokenmaxxing is just one example.
When I saw LLMs becoming more and more capable of coding over the course of the first half of this year, I had a bit of an identity crisis. I have identified with my work for all my career, so much so that I even spent much of my free time writing about it and going to conferences to speak about it. I was proud of the craft of writing code in ways that make it readable and maintainable and this was being taken away from me now. I was at least half-seriously browsing websites about what courses I can take to start a second career outside of tech (physiotherapist was at the top of that list because my career of sitting at a desk hasn’t been kind to my lower back).
The storm (mid 2026)
I only really started to embrace AI a couple of months ago, in the middle of 2026. By then, every engineer had an “AI Wallet” with a generous (but capped) monthly budget for AI usage. In the couple of months since then I never even got close to spending it all. For no other technology I have learned over the years did I have such a big learning budget. Just knowing that I had this budget did make it very easy for me to experiment with different ways of working with AI, though, and I did just that.
Years ago, I started to take notes in Markdown. I organized the files loosely following the “Second Brain” approach and used Obsidian as the “UI” on top of that Markdown brain. When I read a book, I put my notes in there. I have a note for each person I regularly interact with at work. I also took haphazard progress notes for work projects. When I write an article or do some research for a project, I write a draft in Markdown before publishing it (I’m writing this article in Obsidian right now). The problem with this Markdown brain was that I would put all kinds of notes into it, but I never got anything out of it anymore. It was a write-only brain. So, one of my experiments was to use AI to help me get value out of my second brain. I started RovoCLI in the root folder of the Markdown brain and had a conversation with it about how I want to work with this second brain. The result was a couple of skills that interact with my Markdown brain in certain ways, among them:
- A “people” skill with a template for a “person” note and a trigger that allows me to add information to a person’s note by “@-mentioning” them in my conversations with Rovo. In my next meeting with that person, I just open that note and know what I needed to talk about.
- A “project” skill that would manage a project folder for each of my projects and keep a project memory: a log of the decisions and other meaningful progress of that project. It also allows me to spin up a throwaway workspace folder outside of my Markdown vault and inject it with an AGENTS.md file that instructs the workspace agent to write any meaningful project results back into the project memory inside my vault, so the important information doesn’t get lost even when I delete the transient workspace folder.
- The project skill also has a “wrap up” function that archives the project; before it does that, however, it goes through the project data, compares it against the official expectations of a Principal Engineer at Atlassian, and creates a draft for the self-assessment required for the yearly performance cycle. When the performance review comes, I at least have a starting point.
- Simple “upload” and “download” skills that automatically sync my Markdown brain with a Git repository and resolve potential merge conflicts.
I finally got value out of my Markdown notes. My Markdown brain is evolving every week, but I’ve been using it consistently for months now where I had previously switched between different “productivity” systems every few weeks. Part of the reason I’m sticking with it, I think, is because it’s tailored to me. There are no restrictions by any tooling, it’s just files backed by a Git repo and an agent. I’m not using someone else’s skills, but the ones that I created, and I can modify them every time I interact with my agent and want to improve something.
Another (very successful) experiment was for a recent project where we had to integrate a vendor’s system with some of the services making up Atlassian’s cloud infrastructure running on the Google Cloud Platform (GCP). In a nutshell, we had to give the vendor the right permissions for the right resources in the right environments. Historically, Atlassian’s cloud products run on AWS, but we are currently expanding into GCP as well. And because we learned from how we did things on AWS, we do things differently on GCP now and follow a completely different infrastructure architecture (cell-based architecture). For this vendor integration project, I now had to deal with an unfamiliar cloud platform and an unfamiliar architecture distributed across many different infrastructure teams. And I was totally out of my depth because infrastructure has never been my strong suit. The vendor had a Terraform module that would do everything that’s necessary, but we couldn’t use it because our infrastructure didn’t allow it.
I asked my agent to reverse-engineer the Terraform module and list all the resources and permissions we would need to create in our infrastructure. Then I talked to a few people and isolated a handful of code repositories (owned by different infrastructure teams) that were likely relevant for creating these resources. I gave this list of repositories to the agent, asked it to clone them locally and to figure out how to create the infrastructure resources we needed via these repositories. After some in-depth conversations with the agent to understand everything, I asked it to write everything up as an RFC (which I heavily edited—the audience was “humans”, after all, and the Opus model I mostly used at the time was very verbose) and shared it with the teams. They approved and we had a plan that we could execute. In the end, I finished a project successfully across multiple codebases I had never touched before in an area I wasn’t comfortable with, in a fraction of the time it would have taken me to do it by myself.
I didn’t just let AI do the job for me, but instead I paired up with AI to do the work. This is a much more rewarding way of working with AI, because it forces me to go deep and understand what I’m doing. It also increases the odds that any code changes I propose get accepted by other teams because I can explain to them (in human writing) why the changes are necessary.
This was (and still is) an interesting conflict for me: on one hand, I want to get the most out of AI, having it work as autonomously as possible, picking backlog tasks by itself and pushing changes into production without human intervention—on the other hand, I need to understand at least some things enough to be able to communicate them, and this means I can’t just delegate them to an agent.
Another “aha” moment for me was the power I had with cloning multiple repos into the same folder and then starting an AI agent in that folder. I had analyzed foreign codebases with agents before, but I had never pointed it at a bunch of locally cloned repositories to figure out connections and integrations. I have a feeling that monorepos will become even more popular in the future because of the big context they give to AI agents. I just hope that we will also solve the developer experience problems that I associate with large-scale monorepos.
In our team, we are now moving toward an approach where we have one Git repository that acts as the entry point to everything we work on. It has a list of shared skills for tasks we used to do manually, including on-call duties like alert triage and analysis. This repo also has a list with some metadata of all the other repositories that are relevant to the team’s work, so an agent can clone them locally if needed. Whenever I work on something, I start an agent in that repository and go from there. If we need to make code changes to a different repo, it creates a dedicated clone of that repository, so multiple tasks don’t interfere with each other.
Very recently, we made the somewhat radical decision to archive all our operational runbooks and start over fresh (many of our runbooks haven’t aged well and when we pointed agents at them, they were often confused rather than helpful). Whenever we get an alert now, we’ll analyze it together with an AI agent and package anything that can be automated into a skill in our “entrypoint” repository, and then create a runbook that points to those skills. In the future, we plan to use this repository to back a Slack agent for our team’s help channel to improve the quality of our current chatbot, which was often confused by our old and outdated runbooks. I can’t say anything about how successful this experiment will be, but I’m excited to see how it works out.
I was starting to have fun with AI, and not just at work. I got a personal Claude Code subscription and started working on side projects again, after years of not having the energy for any side projects. I rented a small cloud server and installed Claude Code on it, then built a little helper app for a board game I often play for my personal use. Before AI, I would never have put the effort into building an app that I would likely be the only user of. I can ask Claude to make changes to the app from my phone and redeploy… it’s served directly from the server Claude is running on. Superpowers!
I also started creating models for 3D printing with Claude Code (without ever having done any 3D modeling before). While writing the first draft of this article on a rainy Sunday, I sent off a few prompts to Claude to finalize the paintbrush holder we have been modeling and it’s now printing. AI lets me not only create software but also physical items!
In my personal Markdown brain (which has a similar structure to the work brain), I created a “workout” skill that manages my workouts for me, taking into account any notes I took from my physiotherapist sessions. All I have to do now is type into my phone “schedule a workout”, go to the gym, and tick off the exercises in a nice little workout app that Claude has created. The first version of this app was created during a gym session, prompting from my phone between sets.
By now, I consider myself “AI-pilled”. My identity crisis is over. I realized that experienced software engineers will still be needed in the age of AI to decide what is good engineering, even if it’s just to know that a certain cleanup backlog item is not ready to be worked on yet, or to pair up with an AI agent to create an RFC to align on with other teams. We’re not typing code anymore, but we’re guiding AI agents to build successful software.
Where is this going?
While I think there is no future in software engineering without AI, I do see some problems that the industry hasn’t solved yet.
With AI, we can vibe-code prototypes faster than ever. I have seen projects that were vibe-coded in a prompting session or two and then deployed to production. I have vibe-coded some hobby projects myself and never looked at the code. This is great for early feedback. It takes discipline to make the decision to rewrite a successful prototype from scratch if we want it to become a long-lived and maintainable software system. I fear that more software systems than ever will forever stay in the prototype state and waste tokens and human patience in unnecessary maintenance work. Vibe-coding has its place, but it’s not engineering.
What happens to the new generations of software engineers? They will never be forced to think through software engineering problems and architecture at a slow, painful, human pace, but will be fast-tracked with the help of AI. Will they enjoy this work? How will they know what is good engineering and what isn’t? I’m convinced our job will involve more and more defining acceptance criteria for agents and less working with code. But what about the acceptance criteria that we engineers usually only learn about while working with the code?
In “Enterprise” software engineering, we still can’t use AI to the extent I would want to. We have this new superpower that has the potential to make every one of us a 100x engineer, but in some areas—especially when they are focused on Enterprise customers—we are slowed down by multiple human code reviews, the general complexity of a distributed architecture, and manual compliance audits. Auditors are still asking us to make manual screenshots of our backup configuration as evidence that we’re following the standards. I found that practice annoying before AI and even more so now. When will AI arrive in “Enterprise” software engineering to make things easier?
How do we deal with the constant context switching that working with AI agents presents us with? We fire off one prompt, and while waiting for the result, we go to the next agent and fire off a second prompt to solve a second problem. Whole tool suites are being built to allow us to work with as many agents as possible in parallel. When we have arrived at the 3rd tab, we have forgotten about the first. I have been overwhelmed by this many times already, feeling drained instead of energized. Will we find a different way of working with AI that doesn’t require us to constantly switch context? Is the solution to move away from code and towards specifications and tests? Or will we just live with this way of working and get even more scatterbrained than we already are?
Many problems to solve, but what is engineering if not solving problems?
Books by Tom

Get Your Hands Dirty on Clean Architecture
Build web applications with a hexagonal architecture, step by step, with code examples in Java. More than 6,500 readers so far.
Get the book
Stratospheric
From zero to production with Spring Boot and AWS: deploy a Spring Boot application to the AWS cloud and connect it to services like RDS, Cognito and SQS. More than 1,200 readers so far.
Get the book