← writing

// Business & Perspective

I've Been Coding Since 1999. Here's What Hasn't Changed.

Tables became flexbox, jQuery became React, and we still argue about naming things. A tour through GeoCities, Ask Jeeves, admin/admin, and the constants that outlasted all of it.

I started building websites in 1999. I was a kid with a dial-up modem, a text editor (Notepad), and absolutely no idea what I was doing, which, it turns out, was also true of most of the professional web back then. I've been doing this long enough now to watch entire technologies get invented, win, lose, and get reinvented under a different name. Here's what I remember, and what I've noticed stays exactly the same no matter what decade it is.

My first twenty websites were on GeoCities

If you don't know what GeoCities was, imagine someone handed out free apartments in a city made entirely of HTML, organized into "neighborhoods" like EnchantedForest and SiliconValley, and told everyone to decorate however they liked. There was no building code. There were no inspectors. Some houses had working plumbing. Most did not.

My first twenty sites lived there, or somewhere just like it. Every page had a tiled background image, usually something with stars or fire on it, a "Best viewed in 800x600" badge, and a visitor counter that was lying to everyone, including me. I didn't know what I was building toward. I just knew that if I added a marquee tag, the text would scroll, and that felt like power.

The internet had no design, no standards, and no accessibility

People talk about "web standards" now like they've always existed. They have not. Every browser rendered HTML and CSS a little differently, so the job wasn't "write correct code," it was "write code that happens to look right in Netscape, Internet Explorer, and whatever Opera was doing that week." There was no consensus on box models, no consensus on how borders should render, sometimes not even a consensus on what a pixel was.

Accessibility, in the sense we talk about it now, basically did not exist as a practice. Nobody was thinking about screen readers or keyboard navigation. We were thinking about whether the page would render at all on the machine sitting at the library.

And everything, I mean everything, was built with HTML tables. Tables weren't just for tabular data, they were the layout engine. Need a two-column page? Table. Need rounded-looking corners? You sliced a circle into nine little images, dropped each sliver into its own table cell, and prayed none of them had a one-pixel gap. Border radius was a Photoshop trick wearing an HTML costume.

jQuery wasn't a nice-to-have, it was the only way the web worked at all

Before jQuery, writing JavaScript that worked across browsers meant writing the same logic three or four different ways and asking "which browser is this?" before doing anything. There was no shared DOM API you could trust. Internet Explorer had its own event model. Attaching an event listener in IE6 and in Firefox were two unrelated operations that happened to produce similar results if you were careful and lucky.

jQuery's famous pitch, "write less, do more," wasn't marketing fluff, it was a genuine rescue mission. One line of jQuery replaced a dozen lines of browser-sniffing nonsense. That's why an entire generation of developers, myself included, built our whole mental model of "how JavaScript works" around jQuery instead of around JavaScript itself. It wasn't a library bolted onto the web platform. For a long stretch, it effectively was the web platform.

And because there was no real notion of "application state" back then, state just lived wherever was convenient. Sometimes that meant stashing a value in a loose global variable. More often it meant storing the state directly in the DOM: hiding it in a data attribute, stuffing it into a hidden input field, or just checking whether an element had a certain CSS class to decide what the page currently believed was true. The DOM wasn't just a rendering target, it was our database, our state container, and our version control system, all at once, with zero safeguards against any of it.

Search engines rewarded nonsense, and Ask Jeeves was a real competitor

Before Google settled into being the only search engine anyone thinks about, there was a real contest between AltaVista, Yahoo, Lycos, and Ask Jeeves, which let you type a question in plain English to a cartoon butler and get back results that ranged from decent to deeply confused. None of these engines were good at understanding content, which meant the fastest way to rank well wasn't writing good content, it was keyword stuffing.

You'd see a page about, say, a plumbing company, with a block of white text on a white background at the bottom of the page listing fifty totally unrelated words: celebrity names, unrelated cities, products the site didn't sell. None of it was for a human reader. It was bait thrown at an algorithm that couldn't tell the difference between relevance and repetition. It worked embarrassingly well for a while, which tells you something about how early those algorithms really were.

Fake HTML elements failed in 2005 and then quietly won as JSX

Long before JSX, people tried inventing their own HTML tags. You'd see things like <made-up> or custom tag names sprinkled through markup, usually from someone experimenting with templating or trying to make HTML feel more semantic than it actually was. Browsers just rendered the unknown tag as a generic, unstyled element and moved on, so these experiments never really caught fire. There was no spec, no tooling, and no reason for a browser vendor to care.

Decades later, React shows up with JSX, and it's the exact same idea: write tags that aren't real HTML, let a compiler turn them into something the browser actually understands. The difference wasn't the concept, the concept was always fine. The difference was that this time there was a build step, a virtual DOM, and an entire ecosystem willing to support the illusion. Same trick, better plumbing, a decade of patience.

WebGL was supposed to replace JavaScript. It didn't.

At one point there was real excitement that WebGL, or something built on top of it, would become the primary way we built interactive sites, effectively sidestepping JavaScript and the DOM entirely in favor of rendering everything like a game engine. It was going to be faster, more consistent across browsers, and free of all the DOM quirks everyone was tired of fighting.

It never happened. WebGL turned into exactly what it's good at: games, data visualization, creative coding, 3D product viewers. JavaScript and the DOM didn't get replaced, they got better tooling built around them instead. Sometimes the future everyone predicts is real, just scoped down to a tenth of the size people imagined.

Editing a PayPal button and actually getting the product for a penny

For a long time, "Buy Now" PayPal buttons were just an HTML form with hidden input fields, one of which held the price, sitting right there in the page source. Open the browser's developer tools, or even just view source and resubmit the form by hand, change the amount from $49.99 to $0.01, and submit it. On plenty of sites, that actually worked. The payment went through for a penny, and because the backend had no server-side price verification, nothing stopped the order from processing and the product from shipping.

That wasn't a sophisticated exploit. It was editing a number in a form. The entire industry just hadn't learned yet that you cannot trust anything the browser sends you, because the browser is in the user's hands, and the user can open the hood.

admin/admin, and walking straight into the backend of websites

This was the era of the CMS wars: phpNuke, Mambo, Joomla, Drupal, WordPress, and a dozen smaller contenders, all fighting for the same territory. A huge number of sites running any of them shipped with default credentials, and a huge number of site owners never changed them. Username: admin. Password: admin. That was it. You didn't need to hack anything, you just needed to know the login URL and try the two words everyone already knew.

I used to just walk into the backend of websites this way, not maliciously, just out of curiosity about how fragile everything actually was. And it was extremely fragile. Looking back, it's almost funny how low the bar was. Looking back again, it's a little terrifying how much of the early internet ran on that same low bar.

And somehow, out of that entire CMS battlefield, WordPress won. Not because it was the most secure, it very much was not, but because it was the easiest to install, the easiest to theme, and the fastest to get a plugin for whatever you needed. WordPress basically sprang out of nowhere around 2003 and just kept absorbing market share until the "CMS battle" quietly stopped being a battle at all.

The PNG that made your site look expensive, at the cost of your visitor's phone bill

When PNG support became common, it was a genuine upgrade over GIF, real transparency, more colors, cleaner edges. The catch was file size. A nicely designed PNG border, drop shadow, or "layered" edge effect could easily be a few hundred kilobytes, which doesn't sound like much until you remember a lot of us were still on dial-up, downloading a single webpage at the pace of a fax machine.

But if your site had those soft PNG borders making each box look like it was floating slightly above the page, giving the whole layout a sense of depth, you looked like you knew what you were doing. That effect is so completely standard now, it's baked into every CSS framework by default, that nobody would even think to mention it. Back then it was a flex.

Watching AWS become Amazon's real business

I remember when AWS launched and most people treated it as a weird side project from a bookstore. There was no Azure. There was no Google Cloud as a serious competitor. The idea that you could rent a server by the hour, instead of buying a physical box and colocating it somewhere, was genuinely novel. Watching that side project grow into the thing that generates a huge share of Amazon's operating income, more than the retail business a lot of years, has been one of the strangest things to witness up close. Cloud computing wasn't obviously going to work. It just quietly became the default.

Writing SQL by hand was the whole job, because there was no other option

There were no real object-relational mappers in wide use when I started. You wrote SQL. All of it, by hand, including the multi-table joins, the temp tables, and the query plans you tuned yourself because nothing was going to tune them for you. Taking a query against half a billion rows, with five joins and three temp tables, and getting it to return in under half a second, that was a genuinely satisfying skill to have. It felt like craftsmanship because it was craftsmanship.

Now most of us reach for an ORM by default, and honestly, that's usually the right call. But there's a real cost to never having to think about what the database is actually doing under the hood, and I think a lot of engineers today have never had the experience of staring at an execution plan until it told them the truth.

Today's "new" layout tools are yesterday's plugins, formalized

Flexbox used to be something you reached for a JavaScript library to fake. CSS grid used to be something you reached for a JavaScript library to fake too, and Pinterest, more than anyone, really pushed that masonry-grid style of layout into the mainstream before the browser itself could do it natively. AJAX used to be its own special incantation, XMLHttpRequest, mismatched browser support, callback pyramids, before it became fetch, and before "AJAX" quietly evolved into live updates and long-lived polling connections that we just expect pages to have now.

None of these ideas are new. What changed is that the browser eventually absorbed the plugin's job and did it natively, which is a pattern that's happened so many times in this field it should probably have its own name.

We didn't even call it "software engineering"

Nobody called what I was doing in 1999 "software engineering." It was "web development," full stop, and a slightly disreputable corner of it at that. We didn't have the vocabulary that gets thrown around in job postings now: microservices, observability, infrastructure as code, continuous deployment, design systems, developer experience. Not because the underlying ideas were entirely absent, but because the field hadn't yet decided it needed formal names for them, or the maturity to treat them as disciplines rather than habits.

The optimism changed, and that's the part that actually worries me

In the early 2000s, the dominant belief about the internet was genuinely utopian: this thing is going to educate the entire planet for free. Anyone with a connection could learn anything. Knowledge was getting unlocked, not gated. That belief wasn't naive exactly, it was just early.

In 2026, the conversation has shifted to something much more uneasy: most people can no longer reliably tell what's real, and a large share of what they're looking at was generated specifically to be looked at, not to be true. That is a genuinely different problem than anything we were worried about in the GeoCities era. We used to worry about access. Now we worry about trust. That's not a small shift, and I don't think we've built the tools to deal with it yet.

What hasn't changed at all

For everything that's different, a surprising amount has stayed exactly the same.

  • The browser is still the most hostile runtime environment in software. You still cannot trust anything that arrives from the client, whether that's a hidden form field in 2003 or a tampered request body in 2026.
  • Every "revolutionary" new tool is still mostly an old idea with better tooling around it. Fake tags became JSX. JS-powered layout hacks became native CSS. The ideas were rarely new, the execution eventually caught up.
  • Whoever is easiest to adopt wins, not whoever is most correct. WordPress didn't win the CMS wars on security. jQuery didn't win on elegance. Ease of adoption beats technical purity almost every single time.
  • Security is still mostly about people not changing the defaults. admin/admin in 2003 and a leaked API key committed to a public repo in 2026 are the same failure wearing a different decade's clothes.
  • We are still naming things badly and arguing about it. This was true when we were debating whether a function should be called init() or setup() in 1999, and it is true in every single code review I've ever sat in since.
  • And the actual hard part of this job has never been the syntax. It was never the framework of the month. It was always understanding the problem well enough to know what you're actually building, and that part hasn't gotten one bit easier with time.

I don't know what the next twenty-five years of this will look like. I didn't predict any of the last twenty-five either. But if the pattern holds, whatever comes next will feel completely new, and it'll turn out to be an old idea, finally given the tooling to work.

← Back to writing Get in touch