Technological fashion is a tool, framework, or architectural pattern we reach for because it signals modernity, not because it solves a concrete problem better than the alternatives. Technological progress, by contrast, is a durable improvement in the cost, reliability, maintainability, or expressiveness of a system—measurable against a baseline that existed before. The distinction matters deeply for anyone building software that outlives a conference cycle. When we mistake fashion for progress, we accumulate complexity without corresponding value: we adopt microservices before we have a monolith that hurts, we rewrite working backends in the latest language because the old one “feels legacy,” and we bolt on event sourcing to a CRUD app that never needed an audit log. This article is not a polemic against new things. It is a set of heuristics for telling the difference between a genuine step forward and a well-marketed detour, grounded in concrete examples from the last two decades of API design, infrastructure, and programming-language evolution.

The Sociotechnical Gap: Why We Reach for Fashion
Before we can separate fashion from progress, we have to understand why the gap exists. The sociotechnical gap—a term I borrow from the CSCW literature but apply here to the chasm between what software does and what users need—is not just about requirements. It is also about the incentives of the people who build software. Developers, architects, and engineering managers operate inside a reputation economy. A résumé that says “led migration from REST to GraphQL” often reads better than one that says “maintained a boring JSON API for five years with zero downtime.” Conferences need talks about the new thing. Vendors need adoption for their new thing. The result is a constant pressure to adopt technologies whose primary value is social, not technical.
This is not a moral failing. It is a structural property of an industry where the half-life of a framework is shorter than the amortization period of the code written in it. The question is not whether we should ever adopt new technologies. The question is whether we can build the judgment to know when the new thing addresses a real constraint in our sociotechnical system, and when it merely addresses our anxiety about being left behind.
Three Signals of Technological Fashion
1. The Solution Precedes the Problem
Fashion often arrives as a solution looking for a problem. Consider the rise of reactive programming frameworks in the mid-2010s. For systems that genuinely needed to handle millions of concurrent events with backpressure—think telemetry pipelines or live sports scoreboards—reactive streams were a meaningful improvement over thread-per-request models. But the fashion spread far beyond that niche. Teams building internal CRUD apps with a few hundred users started wiring together Mono and Flux chains because it was the modern way. The result was code that was harder to read, harder to debug, and no more performant than the blocking servlet it replaced. The tell: nobody could articulate what constraint the reactive model was relaxing. When you cannot name the specific bottleneck that a technology removes, you are probably dealing with fashion.
2. Adoption Is Driven by Aesthetic Preference, Not Empirical Comparison
Fashion appeals to taste. Progress appeals to measurement. When a team argues for a new database because “the query language is cleaner” without benchmarking it against the existing one under production-shaped workloads, they are making an aesthetic argument. That does not mean the argument is wrong—sometimes a cleaner query language reduces bug rates in ways that are hard to benchmark—but it does mean the burden of proof has not been met. Progress, by contrast, tends to come with numbers: latency at p99.9, throughput per dollar of cloud spend, mean time to recovery after a partition. If the only evidence is a conference talk and a feeling, proceed with caution.
3. The Technology Solves a Problem Created by the Previous Fashion
This is the most insidious signal, because it looks like progress. Kubernetes is a powerful orchestrator that solves real problems in multi-tenant, multi-service deployments. But many of the problems it solves—service discovery, secret management, rolling updates—were problems created by the previous fashion of decomposing applications into dozens of microservices. If your system runs happily on a handful of VMs behind a load balancer, Kubernetes is not progress; it is a second-order fashion, cleaning up after the first one. The heuristic: trace the problem back to its root. If the root is a technology choice that was itself optional, the solution may be to undo the root choice, not to add another layer.

Three Signals of Technological Progress
1. It Makes a Previously Hard Thing Trivial
Progress often looks boring in retrospect. TLS 1.3 (RFC 8446) is a perfect example. It removed obsolete cipher suites, reduced the handshake from two round trips to one, and made forward secrecy mandatory. Nobody gave a keynote about how exciting TLS 1.3 was. But it made every HTTPS connection faster and more secure, and it did so in a way that required almost no application changes. That is the signature of progress: it collapses a complex, error-prone surface area into something simple and safe. In API design, the move from hand-coded OAuth token refresh logic to the standardized OAuth 2.0 Device Authorization Grant (RFC 8628) for input-constrained devices is another example—it took a problem that every IoT team was solving badly and gave them a single, well-specified flow.
2. It Generalizes a Pattern That Was Previously Ad-Hoc
Progress often looks like someone noticing that ten different teams built the same thing ten different ways, and then extracting the common part. The async/await syntax in JavaScript and Python is a good case. Before async/await, developers managed asynchronous control flow with callbacks, then promises, then generator-based coroutines. Each approach worked, but they were inconsistent, hard to compose, and produced confusing stack traces. async/await did not invent new capabilities; it took a well-understood pattern and gave it first-class syntactic support. The result was code that was easier to write, read, and debug—a genuine reduction in cognitive load. When a new technology makes you say “finally, this is how it should have always worked,” you are probably looking at progress.
3. It Is Adopted Because the Old Way Is Unmaintainable, Not Just Unfashionable
Progress is pulled by necessity, not pushed by marketing. The migration from XML to JSON in web APIs is a canonical example. XML was not bad; it was just heavy for the use case. Parsing XML in a browser required a DOM parser, and the data-to-markup ratio was poor. JSON emerged because frontend developers needed something lighter and more native to JavaScript. The adoption was driven by a genuine ergonomic gap, not by a desire to be modern. By contrast, the migration from JSON to Protocol Buffers is progress only when the schema enforcement and binary efficiency actually matter—inside a datacenter, between services you control. Using protobufs for a public API that serves a mobile app is often fashion, because the human-readability of JSON is a feature, not a bug, when debugging integration issues.
A Decision Framework: Questions to Ask Before Adopting
I use a simple set of questions when evaluating a new technology. They are not a checklist that guarantees a correct answer; they are a forcing function to surface hidden assumptions.
- What is the constraint that this technology relaxes? If you cannot name the constraint—latency, throughput, developer productivity, operational toil, error rate—stop. You do not understand the technology well enough to adopt it.
- Is that constraint currently binding on my system? A technology can be genuine progress and still irrelevant to you. WebAssembly is a real advance in portable sandboxed execution. If you are building a Rails monolith, it probably does not matter.
- What is the simplest thing I could do to relax the constraint without adopting this technology? Sometimes the answer is “buy a bigger instance.” Sometimes it is “remove a feature nobody uses.” Exhaust the simple options before reaching for the complex one.
- What is the total cost of adoption, including the cost of un-adoption? Every technology choice is a liability on the balance sheet of your system. The cost is not just the initial integration; it is the ongoing cognitive overhead, the debugging difficulty, the hiring implications, and the migration cost if you need to reverse the decision. If you cannot estimate the exit cost, you cannot estimate the total cost.
- Who benefits from this decision, and when? If the primary beneficiary is the person making the decision, and the benefit accrues immediately (résumé, talk proposal, blog post), while the costs accrue later to the team that maintains the system, you have an incentive problem. Progress benefits the maintainers. Fashion benefits the deciders.

Case Study: The Microservices Watershed
Microservices are the archetypal example of a technology that straddles the line between fashion and progress. The pattern itself—decomposing a system into independently deployable services organized around business capabilities—is a legitimate architectural option with clear benefits for large organizations: independent scaling, team autonomy, and technology heterogeneity. But the fashion of microservices—the idea that every new project should start with a dozen services, a service mesh, and a distributed tracing infrastructure—is a cargo cult.
The watershed moment for any team is when the monolith becomes a bottleneck. That bottleneck is specific and measurable: deployment queues, merge conflicts, scaling costs that are superlinear with traffic. Before that moment, microservices are a net negative. They replace in-process function calls with network calls, which are slower, less reliable, and harder to debug. They require you to solve distributed systems problems—consistency, discovery, fault tolerance—that the monolith gave you for free. The teams that got microservices right—the ones that wrote the blog posts everyone else copied—did not start with microservices. They started with a monolith, felt the pain, and then carved boundaries along natural seams. The teams that got it wrong started with a microservices template and spent two years building a distributed system that served a hundred users.
The heuristic is not “never use microservices.” It is “do not use microservices until the monolith hurts, and when it hurts, be precise about where the seams are.” That is the difference between fashion-driven architecture and constraint-driven architecture.
Case Study: TypeScript and Gradual Typing
TypeScript is a more layered case. When it first appeared, many JavaScript developers dismissed it as fashion—a way for Java developers to feel comfortable in the frontend. But TypeScript addressed a real, measurable constraint: the difficulty of refactoring large JavaScript codebases without a type checker. As codebases grew, the cost of “undefined is not a function” errors at runtime became unacceptable. TypeScript’s gradual typing meant teams could adopt it incrementally, adding types to the most error-prone parts of the codebase first. The tsc compiler also enabled downlevel compilation, letting teams use modern ECMAScript features while targeting older runtimes—a genuine productivity win.
However, TypeScript also has fashion elements. Strict mode—enabling noImplicitAny, strictNullChecks, and friends—is progress. But the ecosystem’s obsession with ever-more-elaborate type-level programming—template literal types, conditional types, recursive type gymnastics—often crosses into fashion territory. When a type signature is longer than the function it describes, and the primary effect is to impress other developers rather than prevent bugs, it has become an aesthetic pursuit. The heuristic: types should reduce the number of possible states in your program. If a type increases the number of concepts a developer must hold in their head, it is fashion masquerading as safety.
Case Study: The GraphQL Divide
GraphQL is a technology that arrived with a clear problem statement: mobile clients on unreliable networks needed the ability to request exactly the data they needed in a single round trip, and REST APIs with fixed resource shapes were forcing over-fetching and multiple requests. That is a real constraint, and for the right use case—a product with many different client views of the same data, where network round trips are expensive—GraphQL is progress. It collapses N+1 client requests into a single query, and it gives frontend teams autonomy to evolve their data requirements without backend changes.
But GraphQL also became a fashion. Teams with a single web client and a backend they controlled adopted it because it was modern, not because they had a mobile app on a flaky 3G connection. They then discovered the hidden costs: the N+1 problem on the server side (solved by DataLoader, which adds complexity), the difficulty of caching (because everything is a POST), the complexity of authorization (because field-level rules are harder than endpoint-level rules), and the operational pain of debugging queries that can be arbitrarily complex. The GraphQL specification (October 2021 edition) is a substantial document, and implementing a compliant server is a significant engineering effort. For many teams, a well-designed REST API with sparse fieldsets and compound documents would have solved the actual problem with a fraction of the complexity.
The heuristic: GraphQL is progress when the diversity of clients and the cost of round trips are the binding constraints. It is fashion when the binding constraint is that the team wants to learn something new.
Building Judgment: A Personal Practice
I do not believe there is a shortcut to good judgment about technology. It comes from building things, watching them break, and understanding why they broke. But there are practices that accelerate the process. One is to study the technologies that lasted. SQL is over fifty years old. The Unix shell is over fifty years old. HTTP is over thirty. These technologies are not perfect, but they have survived because they solve a problem at the right level of abstraction. Understanding why they survived—what constraints they relaxed, and what constraints they left for others—gives you a mental model for evaluating new things.
Another practice is to build the same thing twice: once with the fashionable technology, once with the boring one. The comparison is often humbling. I have built the same API with REST, GraphQL, and gRPC. For the specific use case—a backend-for-frontend serving a single web app—REST was the simplest, most maintainable, and most debuggable option. The other two added complexity without adding value. That experience cost me time, but it bought me conviction.
A third practice is to read the RFCs and the specifications, not just the blog posts. The blog post tells you why the technology is great. The specification tells you what it actually does, and often reveals the complexity that the blog post elides. When I read the gRPC specification and compared it to the simplicity of a JSON-over-HTTP API, the tradeoffs became concrete in a way that no amount of advocacy could obscure.
FAQ
How do I know if a technology is fashion or progress when it first appears?
You often cannot know immediately, and that is fine. The early adopters of a technology are running an experiment on behalf of the industry. The responsible approach is to let the experiment run. Wait for the post-mortems. Wait for the teams that adopted it to write about what broke. A technology that is still universally praised six months after launch is a technology that has not been used in anger. Real progress tends to generate a specific kind of critique: “it solves problem X well, but watch out for Y.” Fashion generates either uncritical enthusiasm or vague dismissal. Look for the specific critiques.
Is it ever okay to adopt a technology just because it is fashionable?
Yes, with two conditions. First, be honest with yourself and your team that you are adopting it for social reasons—learning, hiring, morale—not technical ones. Second, contain the blast radius. Use the fashionable technology in a non-critical path, a side project, or an internal tool. Do not bet the company’s core product on a technology whose primary value is that it makes your engineers excited to come to work. Excitement is valuable, but it should be weighed against the cost of a failed experiment.
What is the most reliable signal that a technology is progress and not fashion?
The most reliable signal is that the technology makes something previously complex so simple that it becomes invisible. When TLS 1.3 shortened the handshake, users did not notice the protocol; they just noticed that pages loaded faster. When async/await landed, developers stopped thinking about promise chains and started thinking about their business logic again. Progress disappears into the background. Fashion demands attention. If a technology requires constant blog posts, conference talks, and advocacy to justify its existence, it is probably fashion. If it just works and you forget it is there, it is probably progress.
How do I push back against fashion-driven adoption in my organization without sounding resistant to change?
Frame the conversation around constraints and tradeoffs, not around the technology itself. Instead of saying “GraphQL is overhyped,” say “our binding constraint is server-side development speed, and I am concerned that GraphQL’s query complexity will slow us down. Can we run an experiment where we build one endpoint both ways and compare the time to implement, test, and debug?” This shifts the discussion from identity—are you a modern developer or a dinosaur?—to evidence. It also respects the possibility that you might be wrong. Sometimes the fashionable thing is also the right thing. The goal is not to avoid new technologies; it is to adopt them for reasons that hold up under scrutiny.
Closing: The Boring Technology Manifesto, Revisited
Dan McKinley’s “Choose Boring Technology” essay remains one of the most important pieces of engineering writing, but it is often misunderstood as an argument against innovation. It is not. It is an argument for limited innovation tokens. Every team has a finite capacity for novelty. If you spend your innovation tokens on a new database, a new programming language, and a new deployment platform all at once, you have no capacity left to innovate on the thing that actually differentiates your product. The art is to be boring everywhere except where it matters, and to be absolutely clear about where it matters.
Distinguishing fashion from progress is not about being conservative. It is about being deliberate. The technologies that constitute real progress—the ones that will still be here in twenty years—are the ones that solve a real problem at the right level of abstraction, with a minimum of ceremony. Everything else is a conversation we are having with ourselves about what kind of developers we want to be. That conversation is not worthless, but it should not be confused with engineering.