In 2006, I was in college, and software development already felt like the future.
Dual-core processors were becoming normal. 64-bit desktops were arriving. AJAX and “Web 2.0” were turning websites into applications. Ruby on Rails was making people rethink how quickly a web application could be built. .NET 2.0 and Java 5 were current. Subversion was the serious source-control choice. Amazon had just launched S3 and EC2.
The tools were not primitive. They were just more separate, more manual, and more dependent on the individual developer keeping track of everything.
I’ve been writing software ever since, and the biggest changes have not all been about programming languages or processor speed. They have been about what happens around the code: how we preserve it, deploy it, search for dependencies, access hardware, and turn an idea into an experiment.
Some old problems have nearly disappeared. Others have been replaced by problems that would have sounded ridiculous twenty years ago.
Here are ten things that stand out to me.
1. Linux is going mainstream
Linux has gone from “the operating system you install if you enjoy fixing operating systems” to a genuinely excellent desktop platform.
KDE is great. Immutable distributions like Bazzite and Aurora are great. Gaming is pushing Linux forward, and AI development is doing the same thing. A growing number of people are using Linux because it is a practical choice, not because they want to spend their weekend compiling a window manager.
There’s also a hardware argument that feels increasingly important. RAM is expensive again. New computers are expensive. Being able to keep using hardware that was perfectly adequate twenty years ago is not a theoretical concern. Linux can do that. Windows increasingly cannot.
I still wouldn’t claim that every Linux desktop is effortless. Hardware support, drivers, firmware, and the desktop stack can still surprise you. But the direction is clear: Linux is becoming a normal option for normal people, and the old assumption that Windows is the only serious desktop operating system is getting harder to defend.
In 2006, installing Linux was often an act of commitment. In 2026, installing Linux can be the practical choice.
2. Agent-driven coding sets me free
As a problem solver, I’m always looking for a problem to solve. This is where the magic happens.
When I have an idea, I can send it to my agent from my phone. By the time I get to my desk, I might have a prototype waiting for me. It may be wrong. It may be incomplete. It may reveal that the idea was bad.
That’s fine. It exists now, and I can respond to something concrete.
This changes the relationship between ideas and action. I no longer have to sit down at a computer before every idea is allowed to become an experiment.
The computer used to be the place where I did all the thinking. Now it is increasingly the place where I review, redirect, and choose between things that have already been explored.
That is a big change for someone like me. I am always looking for a problem to solve, and I tend to find them everywhere. The bottleneck used to be getting enough uninterrupted time at the keyboard. Now I can start the process from almost anywhere.
The agent does not replace judgment. It makes judgment more important. Generating ten possible implementations is cheap. Understanding which one is safe, useful, maintainable, and worth keeping is still a human job.
This is also becoming part of how I work with clients. I can take a vague problem, turn it into a small working experiment, and use the experiment to figure out what the real problem is. If you have a software problem that needs someone to investigate it, my consulting page is here.
3. Source control is solved, more or less
Just use Git.
Git beats walking around with a box full of floppy disks. It beats copying folders into Dropbox. It beats Subversion. It beats naming files project-final-final2-really-final.
For most software projects, source control is no longer an exotic discipline reserved for large teams. It is just part of creating a project.
Git is not perfect. It does not solve backups, secrets, hosting, or preservation by itself. But it solves the basic problem of keeping a history of your work extremely well.
It is also a little funny that GitHub has become so central that its outages feel like shared weather events. Everyone checks whether GitHub is back before deciding whether the workday can resume.
That is especially poignant because Git itself does not need GitHub. The repository on my computer still works when the website is down. The important part of the system does not have to live on the website.
That separation is one of the reasons Git won. GitHub is convenient. Git is the actual source-control system.
The lesson is probably the same as it was with every other hosted service: use the service, but keep your own copy.
4. Digital does not mean preserved
I wish I still had the code I wrote in college.
It was digital. That should have made it easy to preserve. Instead, it lived on hard drives, burned discs, removable media, and whatever backup habits I happened to have at the time.
The problem was not that the code was stored digitally. The problem was that I did not make enough copies.
A digital file feels permanent right up until the disk fails, the backup becomes unreadable, the service disappears, or you discover that the only copy was on a computer you threw away ten years ago.
That experience changed how I think about backups. I now have a backup-everything mentality, but the important part is not merely making copies. It is testing the copies.
A backup that has never been restored is a hope, not a backup.
Source control helps enormously. Put the code in Git. Push it somewhere. Keep another copy somewhere else. Preserve the environment if the project matters. Export the data in formats you can still read.
We have much better tools for preserving our work now. We still have to actually use them.
Digital storage is easier than paper in many ways, but it is also easier to forget. A box of papers sitting in a closet is visible. A forgotten external drive is not.
5. Package managers turned setup into automation
When I was getting started, software arrived on CDs in the back of magazines. Earlier generations had to type programs in by hand from printed listings.
Now I can run a script and get an entire development environment: the compiler, runtime, libraries, tools, services, and sometimes a database.
Package managers, lockfiles, containers, devcontainers, and reproducible builds have changed what “setting up a computer” means. A development environment can be described as code and recreated somewhere else.
That is an incredible improvement.
Of course, we traded one kind of problem for another. A small project can now depend on hundreds of packages maintained by people we have never met, pulled from registries that may change or disappear. The dependency problem did not go away. It became a graph.
There is also a strange amount of ceremony around modern projects. Sometimes a program that does one simple thing needs a package manager, a runtime manager, a build tool, a formatter, a linter, a test runner, a container, and three configuration files.
Still, I would rather debug a dependency graph than hunt through a stack of CDs for the right version of a library.
6. The browser is the game engine and the desktop application
In 2006, the browser was primarily where we viewed documents and interacted with web applications.
Now the browser is the game engine. It is the desktop application. It is a runtime with graphics, audio, storage, networking, video, workers, WebAssembly, and hardware access.
A browser application can talk to Bluetooth devices, gamepads, cameras, microphones, and hardware that would have required a native application not long ago.
The browser is no longer just a window onto software running somewhere else. Increasingly, it is the place where the software runs.
That has made software easier to distribute. I can send someone a link instead of sending them an installer. Updates happen centrally. The same application can work on several operating systems.
It has also made browsers enormous, complicated, and difficult to replace. The browser is now one of the most important pieces of infrastructure on the computer.
There are tradeoffs here. Web applications are convenient, but they often want permanent access to our accounts, data, notifications, camera, microphone, and location. The browser has become powerful enough to replace many desktop applications, which means it has also become powerful enough to become a serious privacy and security boundary.
Still, when I look at what can run in a browser now, it is hard not to be impressed.
7. The cloud is still a server on someone else’s computer
Cloud computing is still, in the oldest joke about it, a server on someone else’s computer.
That description is unfairly dismissive, though. There are very good reasons to use someone else’s computer.
They may have better physical security, better power, better networking, better backups, better monitoring, and people whose entire job is keeping the machines available. I can rent capacity for an hour instead of buying a server, wiring a rack, finding a place to put it, and remembering to replace a failed disk.
That is the good part.
The bad part is that the machine is still someone else’s. The provider can change prices, change policies, have an outage, lock an account, lose data, or make it difficult to leave.
A system spread across several managed cloud services can also become harder to understand than the server it replaced. You may no longer know where the data lives, what happens during an outage, or how much it will cost when usage changes.
Cloud systems are useful because they let us rent capabilities instead of owning all the hardware. They are dangerous when we forget that renting is not the same as control.
I like the cloud best when I understand what I am renting, what it costs, how to get my data back, and what happens when the provider is unavailable.
That last question is the one many systems avoid until the answer becomes urgent.
8. Bad tests are technical debt
Automated testing became much more normal over the last twenty years. That is good.
But having tests is not the same as having useful tests.
Bad tests are technical debt. They can make a codebase slower to change, harder to understand, and falsely reassuring. A test that verifies the wrong behavior is worse than no test in one important way: it tells you that the wrong behavior is protected.
A green test suite only proves that the code matches the tests. It does not prove that the product solves the user’s problem, that the requirements were correct, or that the system will survive contact with reality.
Good tests give you confidence. Bad tests give you ceremony.
There are tests that catch real regressions, and there are tests that mostly document the current implementation. Those are not equally valuable, even if both make the coverage number go up.
I have worked on systems where changing a harmless implementation detail meant updating dozens of tests that were too tightly coupled to the internals. The tests were technically passing, but they were making the code harder to improve.
Testing is a tool for managing risk. It is not a morality play, and it is not a substitute for understanding the system.
The goal is not to have the most tests. The goal is to have tests that catch the failures we care about.
9. Open source became infrastructure
In 2006, open source was already important. But it was still easy to think of it as a collection of projects we downloaded and used.
Now open source is infrastructure.
Nearly every commercial product sits on a huge stack of open source software. Operating systems, databases, web servers, programming languages, cryptography libraries, build tools, container runtimes, monitoring systems, and AI frameworks all depend on projects maintained by people most users will never meet.
That is a remarkable achievement. It is also a fragile arrangement.
Some of the most important software in the world is maintained by a very small number of people, sometimes in their spare time. Companies may build billion-dollar products on top of libraries whose maintainers receive little money, little recognition, and a constant stream of demands.
We have become very good at consuming open source. We are still figuring out how to support it.
There is also a difference between using open source and understanding it. Modern applications may include thousands of dependencies. Most teams cannot personally inspect all of them. We trust package managers, registries, maintainers, scanners, signatures, and the general assumption that nothing terrible is happening inside the supply chain.
Most of the time, that works.
The scale is impressive. The amount of trust involved is more impressive.
10. The job is less about typing code and more about judgment
Code is still important. It may even be easier to produce than ever.
But the hard parts of software development have always been choosing the right problem, understanding the existing system, communicating constraints, reviewing changes, protecting users, and knowing when a proposed solution is quietly making everything worse.
Those parts have not gone away. In some ways, they have become more important.
If an agent can produce a prototype in minutes, the valuable question is no longer only, “Can we build this?” It is also:
- Should we build it?
- What does it need to do?
- How will we know whether it works?
- What happens when it fails?
- Who will maintain it?
- What data does it touch?
- What are we giving up by choosing this approach?
The syntax has never been the whole job. We just have fewer excuses for pretending it is.
The more code becomes something we can generate, the more important it becomes to understand the system that code is joining.
That may be the biggest change between 2006 and 2026. The mechanics of writing software keep getting easier. The responsibility of deciding what software should exist keeps landing on us.
My favorite technology, and my least favorite
My favorite technology of the last twenty years is WebAssembly.
It is one of those ideas that makes the browser feel much larger than it used to be. Code written in languages other than JavaScript can run in the browser, close to native speed, with a defined execution model and fewer assumptions about the host system.
WebAssembly makes it possible to bring compilers, graphics tools, games, emulators, editors, scientific software, and other traditionally native applications to the web. It gives developers a useful target that is not tied to one operating system or one programming language.
It is not magic, and the tooling can still be awkward. But the basic idea is excellent: compile software to a portable runtime and run it almost anywhere.
My least favorite technology is Jenkins.
The worst thing about Jenkins is how wonderful the concept is.
A central system that watches source control, runs builds, executes tests, produces artifacts, deploys applications, and tells you when something has gone wrong? That should be one of the most useful tools in software development.
Instead, Jenkins is one of the worst implementations of one of the best ideas.
It starts innocently. You need a build server. You install Jenkins. Then you add a plugin. Then another plugin. Then somebody configures a job through a web form. Then the job depends on a particular agent, a particular Java version, a particular workspace, and a plugin that nobody remembers installing.
Eventually the build works, but nobody knows exactly why. The configuration lives partly in files, partly in the UI, partly in plugins, and partly in the memory of the person who set it up.
Every Jenkins installation I have encountered seems to be carrying the ghosts of several previous installations.
I know modern Jenkins can be managed more cleanly. Pipelines can be stored as code. Plugins can be controlled. Agents can be disposable. There are disciplined ways to operate it.
I still hate Jenkins.
The problem is not continuous integration. Continuous integration is one of the best ideas in software development.
The problem is taking that idea and implementing it as a semi-haunted Java application whose behavior depends on plugins, mutable server state, configuration archaeology, and whether somebody clicked the right checkbox three years ago.
WebAssembly is my favorite because it points toward a more portable future.
Jenkins is my least favorite because it shows how badly we can implement a wonderful idea.