Ask HN: In what ways is programming more difficult today than it was years ago?
>Also, I think today we’re kind of overburdened by choice. I mean, I just had Fortran. I don’t think we even had shell scripts. We just had batch files so you could run things, a compiler, and Fortran. And assembler possibly, if you really needed it. So there wasn’t this agony of choice. Being a young programmer today must be awful—you can choose 20 different programming languages, dozens of framework and operating systems and you’re paralyzed by choice. There was no paralysis of choice then. You just start doing it because the decision as to which language and things is just made—there’s no thinking about what you should do, you just go and do it.
For context this book is copyrighted 2009 so this interview is more than a decade old, and I'm sure many things have changed since then.
323 comments
[ 2.9 ms ] story [ 218 ms ] threadI sure wouldn’t want to write the backend of a crud app in Fortran, nor the front end. Or do anything with Fortran besides scientific computing (Fortran = Formula Translator…it was built with a limited use case in mind!)
Companies that adapt to newer frameworks and not write everything in C++ are more efficient, but only if they control how much variation of tooling there is within a discipline.
So today, you do have to specialize in a discipline a bit more (front end, backend, data) but each discipline has a sensible set of tools IMO. A developer can and should get some exposure to a secondary discipline to be well rounded and “T-shaped”, but should also appreciate the value of specialization.
More like 4 or 5 distinct sets of tools and that collection changes every few years.
Evolution is generally good.
So, yes, within a discipline there is more than one tool, but often only 1 or 2 current tools that are mature and worth using if you’re starting a new project
It makes little sense to violate everything we know about language design (esp. regarding simplicity and orthogonality) and the cognitive limitations of humans (esp. developers) and keeping the Frankenstein language alive.
It got hashtables in its standard library only when everyone and their dog had already been forced to implement their own for 15 years!
And STL is so complicated that Stroustroup joked that he couldn't have done it if Alex Stepanov hadn't been able to pull it off. That may be a compliment to Stepanov's intellect, but it isn't a compliment for C++'s design.
The evolution of any language inevitably adds reams of extensions, variations, and libraries. This makes the tool not only a lot more heavyweight, but much slower and harder to master, and personally, a lot less fun to use. Give me a tiny simple language (e.g. C) any time over a giant language that requires me to navigate multiple programming paradigms and layers of abstraction (e.g. C++).
Modern languages are like having to speak in Latin. You spend all your time trying to please an nazi grammarian, rather than speaking simply and naturally, as the language was originally conceived.
And those things, frameworks and platforms, are the biggest technical burdens for programmers today. The ability of the web to create what we used to call "interactive apps" (but are today just apps) has lead to the desire and expectation that all web content will take on this level of polish and appearance. While that's possible, it's also arduous. In today's world, you must also learn the frameworks, tooling, and CI/CD processes that lead to your work making it onto someone else's screen. That's a whole lot harder than what we used to do -- like when publishing meant copying a floppy and putting it in an envelope, for example.
The ability of platforms to change their specs and rules all the time (and their insistence on doing so) is another new programmer burden. In the old days, one could write to a piece of hardware or OS and expect that code to run for a long time. Not so anymore. Now the hardware is virtualized and many layers of middleware and SaaS are required to make your code do anything. All of those are moving targets and will change out from under you, without your desire or permission, while you try to continue to deliver new code and service your old code.
Finally, and this is the final nail in the coffin for some more experienced programmers, the misunderstanding of the concept of "agile" or "XP" and how it became scrum -- a series of micromanagement theatrics and paperwork pushing -- has made programming a lot more difficult and a lot less fun. One of the best parts of software development was the unpredictability and experimentation that led to innovative results and small, incremental improvements. Close contact with customers was also a hallmark of early software development. Today's top-down, "How long will it take you to write and debug a feature that doesn't exist?" management mentality cannot and does not lead to quality software, as everyone can tell. It does lead to programmer burnout, quiet quitting, and a lot of wasted time in dev shops.
This sounds precisely correct from my experiences lately. At some version of scale, one simply cannot run the application on just a workstation. The complexity of distributed systems has gone way up, primarily in my opinion, because it solves an organization’s problem, not an individual programmer’s.
qBasic was installed on windows machines and didn't require the internet.
People on mobile don't have a keyboard that makes programming fun.
You needed to program to get your computer to fully work before.
It was easier to learn before because you had to and programming was part of operating a computer.
In practice, a lot of these choices are already made of us. When you join a new project, the space of choice is limited. But when choice is too be made, what I find difficult is that you need to reach a consensus with your colleagues. When you're introverted, it's taxing. There's always some person that needs extra convincing.
Code review wasn't as pervasive and can be tiring too. Sometimes you need to explain again and again why you made that decision (it was in a design document, it was discussed in a meeting, and then the question is brought again in the code review).
But the worse part for me is the accumulation of abstraction layers and dependencies. I'm working on a project with tons of internal dependencies that are loosely specified and documented, all of them introduce some unreliability. The whole edifice is fragile, and yet is expected to work 24/7. This causes a lot of stress.
Yes, open source has a lot of positives, but the bad part is how it has created a culture that incentivizes such a huge amount of dependencies for every project. It seems that for any new requirement the only possible solution is to add a new dependency on yet another library. This results in fragile builds and daily changes caused by dependency updates. In some cases it causes as much troubles as the perceived benefits.
We have way better resources for learning but also much higher expectations. You need to be able to write good code without much thought so that you can maintain a higher level of context while programming. Sometimes you get to write just a nice little isolated bit of code, but usually there are a lot of moving pieces. (no matter how functional and immutable we try to make it.)
Maybe about 20 years ago, if you had a website that allowed users to post a comment and upload a small jpeg, it was considered crazy bonkers cool.
Today, you probably need some advanced 3d UI that communicates with your phone and millions of users in real-time with geotracking and 100 other features to get a pizza to your door in under 5 minutes to barely raise an eyebrow about the technology.
It’s great to have quick solutions to small problems, but too often newbs stop with the SO answer and move on, which means while they’ve solved their problem in the moment they never spend the time to actually learn the underlying reasons why the solution worked or explore the issue deeper to actually achieve understanding.
The good software engineers I know are all yak shavers. They’re willing to spend extra time learning how something really works and ensuring they fully understand an issue and its solution, even if the issue is just a small part of what they need to accomplish.
However, everything feels vastly more complicated. My friends and I would put together little toy websites with PHP or Rails in a span of weeks and everyone thought they were awesome. Now I see young people spending months to get the basics up and running in their React front ends just to be able to think independently of hand-holding tutorials for the most basic operations.
Even business software felt simpler. The scope was smaller and you didn’t have to set up complicated cloud services architectures to accomplish everything.
I won’t say the old ways were better, because the modern tools do have their place. However, it’s easy to look back with rose-tinted glasses on the vastly simpler business requirements and lower expectations that allowed us to get away with really simple things.
I enjoy working with teams on complex projects using modern tools and frameworks, but I admit I do have a lot of nostalgia for the days past when a single programmer could understand and handle entire systems by themselves because the scope and requirements were just so much simpler.
Frontend devs who were present before the advent of the major web frameworks, and worked with the simplicity of js script + DOM (or perhaps jquery as a somewhat transparent wrapper) benefited from seeing the evolution of these frameworks, understanding the motivations behind the problem they solve, and knowing what DOM operations must be going on behind the curtain of these libraries. Approaching it today not from the 'ground up' but from high level down is imo responsible for a lot of jr web devs have surprising lack of knowledge on basic website features. Some, probably a minority, of student web devs may get conditioned to reach for libraries for every problem they encounter, until the kludge of libraries starts to cause bugs in and of itself or they reach a problem that no library is solving for them. I feel like this is particularly bad outcome for web devs because web I feel is uniquely accessible for aspiring developers. You can achieve a ton just piggybacking off the browser and DOM and it's API, the developer tools in the browser etc. But not if you are convinced or otherwise forced to only approach it from the other side -- running before you crawl, or trying to setup a webpack config before you even understand script loading, etc.
About half the time you can’t because it’s not actually a form, and they forgot to add handler for enter.
Browsers have a standard, default, expected behavior. Sometimes, in rare cases, it makes sense to break that in order to do something else instead. But you shouldn't just silently break it for no reason other than to confuse the user.
[0] https://www.theworldsworstwebsiteever.com/
Desktop frameworks “committed” input on enter and most often focused the next field, so you could skip them by enter enter enter. This worked correctly since FoxPro/TurboVision/Norton times. Only Ctrl-Enter would press a “default” button out of order.
But web has its own ways as usual.
So perhaps that isn’t the best litmus test? I wonder if it’s written down that should work as part of the assignment.
Breaking default browser behavior because you find it unintuitive is generally a bad idea.
Now if you’re dealing with something that isn’t really a web form—as in you’re overloading input fields for some interactive non form like behavior—then I can see it.
In my case, if the person had some well thought out reason for doing it, I might let it slide. But the vast majority of times I’ve seen it, it’s because the person doesn’t even know how to use forms. Not understanding the underlying technology at at least a very basic level is a strike against you in my book.
Again, it’s not that I disagree with most of your premise, but in fact, as the comments (not just mine) show - people have issues with submitting too early, so it can be a good decision to break that depending on context. Fewer errors. Which is why I brought up how the problem is presented to the candidate. You might be filtering out people based on something less universally accepted and understood than you think.
If you accidentally break it because you don’t understand it, that’s a strike against you in my book. If you consciously break it for a good reason and can coherently defend that reason, that’s a different story.
At least you'd have to get a sample big enough to make a statistically significant conclusion.
And the burden of proof is on you, because you are changing the default, not the other way around.
Think of any other products, they all have established default behaviours, yet it's also very easy to find someone who's never used a product before and finds it "unintuitive".
And intuitiveness is only one part of usability anyway.
When doing a take home exercise the candidate is desperate to figure out what you are judging on. The more you make that explicit the better your outcomes will be.
Right now your process biases for enter handler adders. Is that your intent?
When I give candidates prompts / questions / scenarios, I try to include some specific instructions, but also leave some details out. The idea is to come up with a prompt that junior developers can still work with by going head-on, but which has some open-ended nature to it, to see how people respond to things which are open-ended or even ambiguous. It's not a trick or trap; candidates are explicitly told what's up, and that I'm evaluating not only their ability to solve a problem, but to define the problem in the first place.
If you want to evaluate a candidate's ability to cut through ambiguity or manage sprawling scope, evaluate that and only that in a specific exercise. Don't just build a shoddy coding test and then rationalize it's weaknesses by saying that good candidates will succeed despite the flaws of the test. That's both disrespectful and un-rigorous.
Junior developers get near-real-time feedback from senior developers who are supervising them. Senior developers have to be able to give that feedback, have to be able to anticipate user needs, and have to be able to run long-term projects where you may have to work for days, weeks, or months before receiving critical pieces of feedback.
> If you want to evaluate a candidate's ability to cut through ambiguity or manage sprawling scope, evaluate that and only that in a specific exercise.
This is a common mistake that I see inexperienced interviewers make. Trying to throw more detailed and specific exercises at candidates is a fool's errand at best, and at worst it means that you're putting candidates through additional tests (and your acceptance rate will suffer).
The main problem with ambiguity is that it appears unexpectedly. If you give someone an ambiguous problem and say, "Tell me what is ambiguous about this problem," then you're not testing what you want to know. What you actually want to know is whether candidates can recognize ambiguous problems without being prompted to recognize them--and the reason for this is that ambiguous are extremely common in real-world scenarios.
An ambiguous problem is not a trick or a trap. It is explicitly part of the interview process, and interviewees are given guidance that the problems they are given may not be precisely defined.
> Don't just build a shoddy coding test and then rationalize it's weaknesses by saying that good candidates will succeed despite the flaws of the test.
Why do you say that the coding test is shoddy?
My observation is that a large percentage of candidates will succeed at coding tests if you give them an ambiguous prompt. In practice, they will either ask questions to resolve the ambiguity, or just pick a way to resolve the ambiguity for the purposes of the test. This matches real-world scenarios--you are going to often encounter ambiguous or incomplete problems in the real world.
If you want a precisely-specified coding problem, then go to Hacker Rank or Project Euler or something like that, or join a competitive programming team.
Have you tested this assertion with data? Because I’ve built interview pipelines several times now and the data I collected showed the exact opposite. The more specific a test was for the trait you wanted to select for the better results you’d get across all metrics, interviewers and candidates. It’s almost my defining characteristic of a good selection criteria after 2 decades of interviewing.
My personal experience is that people new to interviewing are the ones who think that making individual interviews more precise will improve the process, but my experience is that improvements to the overall interview process aren’t done by improving how good individual interviews are.
But, the format is not really the point. The point is to have a specific thing you are trying to discern from your filter and to focus your efforts on making that the only thing you are judging on.
Sometimes it makes sense to do something else instead, but if so, you should handle it in a sane way and actually do that something else. Not just suppress it.
Be right back, writing a new design doc for a messaging service and protocol spec to go with it that I can use to pad my next review cycle.
Newbies drop directly to webpack/react and are overwhelmed or having a lot of trouble getting some details right.
Unfortunately a lot of details that are lost in time are accessible if you go through "build stuff basically from ground" so understanding first "why" we needed these frameworks and what were problems to solve.
There is also bunch of people who go to rediscover basic stuff and they claim "you don't need a framework - vanilla js is enough" - but they also miss context and did not run into problems that were pain points before we had frameworks.
I teach an online web development course for a university as a side gig. Our students are forbidden from using any third party code, libraries, frameworks, etc. They have to do everything with native html, css, and JS.
On a somewhat related but tangential note: I also now have clients demanding certain programs and ecosystems, which is absolutely ridiculous to me. The software I use to give you a final cut should in no way be determined by you. Yet somehow we have ceded that ground!
The litmus test for abstractions getting out of control is if a KPMG consultant brings it up while sipping Gin and tonic in business class.
Another problem is explosion of DSLs. Everything is yaml and you spend ages learning things like Terraform and Docker compose yaml syntax.
As far as Terraform/HCL and CloudFormation/Yaml, the alternative is writing code to do the same thing in your language of choice using the CDK with either CloudFormation or the recently ported Terraform/CDK.
But if you do "a little bit of everything" and infrastructure on the side, you're bound to become a master of none.
One of the worst experiences in programming is writing CI/CD pipelines. One wonders why...
Like I wrote in my comment, how much specific things you need to know will depend on how many specific tasks you perform. If you specialise in nothing, everything will have depths unknown. It will also dilute attention/focus which in turn means you'll never be able to fully understand a specific domain or application. This was reflected in https://news.ycombinator.com/item?id=33056052 where the path it took for many developers and engineers in general to find a fitting solution is unknown to newcomers and also simply not taught in favour of delivering "reviewables" in hopes of a positive review (https://news.ycombinator.com/item?id=33056705) .
Sidenote: mapping ports used to be rather verbose, you'd have to include the address family, the address and the port, on both sides of the mapping. That's 6 elements (excluding separators). So most applications including docker made various parts optional. You can map two ports to any interface, or opt to specify one interface but not the other one etc. A novice user of a new application might be best served by not using any shorthand forms and only using the fully qualified names everywhere. By spelling out every option explicitly (including optional values) there is no more guessing what may or may not have been configured.
As much as people decry the internet and it’s role in modern society, I’m glad that I generally have easy access to massive archives of knowledge.
Something I am appreciating about Svelte/Kit and Phoenix is that they are admitting what was right about PHP and server-rendered webpages. I'm a frontend engineer and think that SPAs have their place, but frontend JS represents a kind of tyranny seen nowhere else in tech.
Agreed, LAMP was just so damn fun in how you could go from zero to a fully functioning site in a day or two. Having to manage all the statefulness of fetching & displaying data asynchronously from the client adds an incredible amount of complication both in theory and in practice.
Also agree that the old tech wasn't necessarily better either - but it sure would be cool if someone could replicate the developer experience from back then and produce a result that's up to modern engineering and UX standards.
The more complexity we can now handle, the more complexity we will create.
This made me remember how acceptable a developer designed utility used to be. Now, you can barely launch an MVP without finding or paying for design and high quality UX/UI. If you do it’s likely going nowhere in terms of traction. I’m sure this has only seemed to be the new rule and there are a few exceptions. But not many.
Even Stripe all those years ago really took off after investing in design. They’ve remained rather polished. But they’re also an exceedingly well funded operation.
Gave me flashbacks to when I was younger. I would work on a problem until hitting a wall I couldn't get around then go into Books A Million with pencil and paper and copy concepts/algos out of CS books. I was too poor to spend $50+ on a book at the time. Now, anything a new programmer wants to know is just a Google search away.
But, there is so much more that just diving in can be hard. Even simple things are complicated I think mainly because expectations are so much higher.
I agree the compensation is higher. I don’t agree the respect is any higher. Software engineer is highly associated with terms like neckbeard, redditor, incel, autistic, etc.
I’m extremely hesitant to tell anyone I’m a software engineer. If anything - I’ll lie and say I do product management just to avoid the association. People treat me way better when I say I’m a PM instead of an eng.
You used to know and a few basic APIs (your OS, your stdlib…). You’d probably spend more time implementing basic utilities. However now you have to manage a complex software supply chain of dependencies of varying quality and security risk.
Now with dynamic languages, interpreters, docker, kubernetes, AWS and layers of dev tools and frameworks it can be harder to know what your code is actually doing. But those abstractions can also give you superpowers.
The tools more often than not want me to pull my hair out (coded mostly in Tcl...), the technical forums and support are often non-existent, and the technologies quite often haven't been update with QoL changes in 20+ years.
Devices have become more complex and interconnected. Expectations for software are also higher, while there’s more resources poured into finding and exploiting vulnerabilities.
Keeping on top of all of that, isolating your memory access and permissions, etc… is a lot harder today than it was even a few years ago.
In every other way I think programming is easier now. Languages are more ergonomic, even the ones that are decades old. Libraries are more easily available and there are tons more resources today than ever before for learning.
1. Concurrency. Multiple cores are a completely normal thing now, so having to think about how different threads may interact went from a theoretical concern to a very practical one.
2. Dependencies. Back then you could just turn on the computer and start coding. Today many things have large amounts of dependencies that need installing, compiling or setting up. Weird problems can happen. I have an issue where VS Code just refuses to autocomplete in the test section of my project. Why? I have no clue, and VS Code is a giant of a thing. It's quite easy to spend days or even weeks trying to set things up and work out issues with things that aren't even the thing you were trying to write.
3. Teamwork. Modern computers allow for large programs, which require teams to develop. A lot of the work in building modern successful software is in organization, record keeping, documentation and working with other people.
4. Security. Pretty much everything interacts with outside untrusted inputs, and so it's far more important than before to treat every input correctly. Anything from image loaders to parsers to APIs may be exploited.
That experience is why I relentlessly bang on the drum of "do not swallow errors" because it makes troubleshooting indescribably hard
I would guess the more links in the call/dependency chain, the more opportunities for misplaced assumptions or laziness to sneak in, leading to your cited outcome
Distributing native apps has gotten harder in some ways with code signing required in order to share binaries without scary pop ups or the OS blocking outright.
In 4 days worth of work now, I couldn't even do an evaluation of which UI tech is still going to be around in 2 years...
These tools continue to get better every day. The target is only "good enough" and once reached it presents an outsized advantage over custom builds.
I fully expect no-code/low-code to grow in nearly permanent ways within many organizations.
https://apex.oracle.com/en/
The demo video takes you through creating a full blown database backed app with maps and other geo features, from nothing more than an initial CSV file. The code backing the app is represented as database tables, so you can use queries to explore the app.
The core issue is really the same one the no-code/low-code platforms have always had, or even that VB6 had - the ramp isn't smooth. Eventually you hit the limits of the tool or there's a business requirement the tool can't meet and you get stuck. Often that requirement may be something indirect and non-obvious, like growing the team to the point where you start needing 'real' abstractions, or keeping up with some new feature the underlying platforms added that competitors are exploiting but which aren't exposed. Hence why so many companies have mobile apps that are just ordinary Android/iOS apps instead of written using low-code tools.
It's really hard to build something generic enough. MS Access was as near to it as was feasible I suspect.
BUT the performance is _very bad_ if you don't throw money at oracle. The final nail in the coffin was when customers want you to build something that the blackbox doesn't offer. You are in for a wild wild ride, because whatever happens in the backend is undocumented, you cannot debug it properly.
The bigger pain, and one reason why desktop apps became less popular, is that with the rise of macOS and (to some extent) Linux, you need to distribute your app to three platforms all of which use different code signing technologies and approaches, none of which are portable/standards based or convenient. Also, software update was ignored by platform vendors.
Nowadays things are a bit different. Windows got MSIX, which is a real package manager and which can silently upgrade apps in the background on a schedule even if they're currently running. macOS has the widely used Sparkle framework for updates and of course Linux has had updating package managers for a long time.
Up until recently it was still a pain to actually use all those technologies, even though maybe developing your {JVM,Electron,Flutter,native,etc} app was itself quite pleasant and easy. My company has made a tool to fix that [1] and so you can now build self-updating Windows/Mac/Linux packages from your app files and all the signing is handled for you locally. It's an abstraction over the platform-native distribution technologies designed with an obsessive focus on being as simple as web app distribution is.
Making this stuff easy in turn opens up possibilities for (re)simplifying the dev stack. For example, in some cases you could now make an app that just logs in directly to your RDBMS. No need for a backend/frontend, REST, JSON, web server frameworks, giant JS transpiler pipeline etc. Just use a real GUI toolkit and connect it directly to the output of queries. Any privacy or business logic can be implemented this way using a mix of row-level security [2], security-definer stored procedures [3] and RDBMS server plugins (e.g. [4] or [5]). There are lots of nice things about this, for example, it eliminates a lot of nasty security bugs that are otherwise hard to get rid of (XSS, XSRF, SQLi etc).
[1] https://www.hydraulic.software
[2] https://www.postgresql.org/docs/current/ddl-rowsecurity.html
[3] https://www.postgresql.org/docs/current/sql-createprocedure....
[4] https://tada.github.io/pljava/
[5] https://pgxn.org/dist/plv8/doc/plv8.html
When I first started programming all of my tools had a simple workflow:
* Write a single text file
* run a single command to build (cc thing.c)
* Run the resulting file as a standalone command
People learning to program are often new in general. They're figuring out their text editors. Figuring out how to run programs. Figuring out so many basic things seasoned developers take for granted.
I became quite fluent in C, writing many, many useful programs with just a single text file. By the time I had a need to learn about linking multiple files in large projects I was already fluent and comfortable with the language basics.
Contrast this with modern environments: I need to learn whole sets of tools for managing development environments (venv, bundle, cargo, etc etc etc). These development harnesses all change rapidly and I am constantly googling various sets of commands and starter configs to get things running. These are all things that a seasoned developer will be constantly dealing with on a complex project, but it seems like little effort has been put into creating basic defaults to simplify things for beginners.
Yes, most commercial software packages were written in C. They certainly weren't in one file. They were large systems that took hundreds, sometimes thousands of files and 100k's to millions of lines of code. If anything, we had to write more code to do things because pre-packaged libraries weren't as comprehensive back then. I still remember waiting hours and hours for our application to build. And the old timers told us that that was blazingly fast, lol.
I would agree with a previous poster that there are many more choices today. And I guess if you suffer from a fear of making the wrong choice, that is a problem. But the other side of that is that literally thousands and thousands of examples and even robust code libraries are now available for free that you can drop in and use. That is a HUGE plus.
The interface for beginners scaled all the way down to a very basic single text file, and most beginners would program for months or even years without using those things. It wasn't necessary to teach these tools in school - you could complete an entire degree writing single-file C programs without ever using an IDE.
Many utilities were distributed as a .c file and a Makefile and that's it (before the rise of autoconf)
all: cc myprogram.c
clean: rm -f myprogram.o myprogram
They're extremely simple in simple scenarios. It's just a simple format for writing down the commands you run while working.
Back in my day, being on the path was sufficient. And it is for all the other Java projects. I spent too long sorting that one out.
But I think you're right in many ways though. For seasoned devs, cli tools give a lot of flexibility and allow dev tasks to be automated in pipelines more easily, but it requires one to read documentation or use the help subcommand to discover what else you can do. Which is not a big deal when you are experienced, but I definitely remember struggling early on with things like that when I was self-teaching how to program.
My tongue is in my cheek, but I often wonder.
Typing this one magic word brought up an IDE, including an editor with highlighting, an interactive help system, samples, an in-editor REPL, and single-key shortcut to run the program. I can't remember if it also came with a debugger and a way to create stand-alone executable, or if that came later.
It had built in commands for drawing, input and sound, all well documented. And the UI was straightforward and intuitive.
This doesn't really exist anymore.
It didn’t, it came earlier: QBasic was (and is, it stopped being part of the default install with Win2k but is still available for current Windows OSs) a stripped down interpreter-only version of QuickBASIC, a compiled BASIC.
A plug-in or add on for the tutorial on how to make HTML/JS pages right there inside the browser.
That's not quite like QBASIC was though. Making webpages is just a dim shadow of what QBASIC and controlling your computer was.
Typing this command brings up an IDE, including an editor with highlighting, an interactive help system, samples, an in-editor REPL, and single-key shortcut to run the program, tabs, step-through debugger, breakpoints, intellisense, snippets, extension system.
It has access to the .NET framework that C# uses such as System.Windows.Forms, System.Drawing, System.Console.Beep, System.Media.SoundPlayer, and the UI is straighforward with an editor and a console pane. Library is well enough documented if you can use MSDN website, but certainly not as simple as BASIC and SCREEN 12, LINE (10,10)-(20,20).
This does really exist, but it's deprecated despite being powerful, simple, convenient and useful. Instead the recommended path is to download and install a new PowerShell, VS Code and some VS Code extensions, to get a less integrated, more complex, not-bundled setup.