IDE Culture vs. Unix philosophy

Even more of a hot topic than programming languages is the interactive development environment, or IDE. Personally, I’m not a huge fan of IDEs. As tools, standing alone, I have no problem with them. I’m a software libertarian: do whatever you want, as long as you don’t interfere with my work. However, here are some of the negatives that I’ve observed when IDEs become commonplace or required in a development environment:

  • the “four-wheel drive problem”. This refers to the fact that an unskilled off-road driver, with four-wheel drive, will still get stuck. The more dexterous vehicle will simply have him fail in a more inaccessible place. IDEs pay off when you have to maintain an otherwise unmanageable ball of other people’s terrible code. They make unusable code merely miserable. I don’t think there’s any controversy about this. The problem is that, by providing this power, then enable an activity of dubious value: continual development despite abysmal code quality, when improving or killing the bad code should be a code-red priority. IDEs can delay code-quality problems and defer macroscopic business effects, which is good for manageosaurs who like tight deadlines, but only makes the problem worse at the end stage. 
  • IDE-dependence. Coding practices that require developers to depend on a specific environment are unforgivable. This is true whether the environment is emacs, vi, or Eclipse. The problem with IDEs is that they’re more likely to push people toward doing things in a way that makes use of a different environment impossible. One pernicious example of this is in Java culture’s mutilation of the command-line way of doing things with singleton directories called “src” and “com”, but there are many that are deeper than that. Worse yet, IDEs enable the employment of programmers who don’t even know what build systems or even version control are. Those are things “some smart guy” worries about so the commodity programmer can crank out classes at his boss’s request.
  • spaghettification. I am a major supporter of the read-only IDE, preferably served over the web. I think that code navigation is necessary for anyone who needs to read code, whether it’s crappy corporate code or the best-in-class stuff we actually enjoy reading. When you see a name, you should be able to click on it and see where that name is defined. However, I’m pretty sure that, on balance, automated refactorings are a bad thing. Over time, the abstractions which can easily be “injected” into code using an IDE turn it into “everything is everywhere” spaghetti code. Without an IDE, the only way to do such work is to write a script to do it. There are two effects this has on the development process. One is that it takes time to make the change: maybe 30 minutes. That’s fine, because the conversation that should happen before a change that will affect everyone’s work should take longer than that. The second is that only adept programmers (who understand concepts like scripts and the command line) will be able to do it. That’s a good thing.
  • time spent keeping up the environment. Once a company decides on “One Environment” for development, usually an IDE with various in-house customizations, that IDE begins to accumulate plugins of varying quality. That environment usually has to be kept up, and that generates a lot of crappy work that nobody wants to do.

This is just a start on what’s wrong with IDE culture, but the core point is that it creates some bad code. So, I think I should make it clear that I don’t dislike IDEs. They’re tools that are sometimes useful. If you use an IDE but write good code, I have no problem with you. I can’t stand IDE culture, though, because I hate hate hate hate hate hate hate hate the bad code that it generates.

In my experience, software environments that rely heavily on IDEs tend to be those that produce terrible spaghetti code, “everything is everywhere” object-oriented messes, and other monstrosities that simply could not be written by a sole idiot. He had help. Automated refactorings that injected pointless abstractions? Despondency infarction frameworks? Despise patterns? Those are likely culprits.

In other news, I’m taking some time to learn C at a deeper level, because as I get more into machine learning, I’m realizing the importance of being able to reason about performance, which requires a full-stack knowledge of computing. Basic fluency in C, at a minimum, is requisite. I’m working through Zed Shaw’s Learn C the Hard Way, and he’s got some brilliant insights not only about C (on which I can’t evaluate whether his insights are brilliant) but about programming itself. In his preamble chapter, he makes a valid insight in his warning not to use an IDE for the learning process:

An IDE, or “Integrated Development Environment” will turn you stupid. They are the worst tools if you want to be a good programmer because they hide what’s going on from you, and your job is to know what’s going on. They are useful if you’re trying to get something done and the platform is designed around a particular IDE, but for learning to code C (and many other languages) they are pointless. [...]
Sure, you can code pretty quickly, but you can only code in that one language on that one platform. This is why companies love selling them to you. They know you’re lazy, and since it only works on their platform they’ve got you locked in because you are lazy. The way you break the cycle is you suck it up and finally learn to code without an IDE. A plain editor, or a programmer’s editor like Vim or Emacs, makes you work with the code. It’s a little harder, but the end result is you can work with any code, on any computer, in any language, and you know what’s going on. (Emphasis mine.)

I disagree with him that IDEs will “turn you stupid”. Reliance on one prevents a programmer from ever turning smart, but I don’t see how such a tool would cause a degradation of a software engineer’s ability. Corporate coding (lots of maintenance work, low productivity, half the day lost to meetings, difficulty getting permission to do anything interesting, bad source code) does erode a person’s skills over time, but that can’t be blamed on the IDE itself. However, I think he makes a strong point. Most of the ardent IDE users are the one-language, one-environment commodity programmers who never improve, because they never learn what’s actually going on. Such people are terrible for software, and they should all either improve, or be fired.

The problem with IDEs is that each corporate development culture customizes the environment, to the point that the cushy, easy coding environment can’t be replicated at home. For someone like me, who doesn’t even like that type of environment, that’s no problem because I don’t need that shit in order to program. But someone steeped in cargo cult programming because he started in the wrong place is going to falsely assume that programming requires an IDE, having seen little else, and such novice programmers generally lack the skills necessary to set one up to look like the familiar corporate environment. Instead, he needs to start where every great programmer must learn some basic skills: at the command-line. Otherwise, you get a “programmer” who can’t program outside of a specific corporate context– in other words, a “5:01 developer” not by choice, but by a false understanding of what programming really is.

The worst thing about these superficially enriched corporate environments is their lack of documentation. With Unix and the command-line tools, there are man pages and how-to guides all over the Internet. This creates a culture of solving one’s own problems. Given enough time, you can answer your own questions. That’s where most of the growth happens: you don’t know how something works, you Google an error message, and you get a result. Most of the information coming back in indecipherable to a novice programmer, but with enough searching, the problem is solved, and a few things are learned, including answers to some questions that the novice didn’t yet have the insight (“unknown unknowns”) yet to ask. That knowledge isn’t built in a day, but it’s deep. That process doesn’t exist in an over-complex corporate environment, where the only way to move forward is to go and bug someone, and the time cost of any real learning process is at a level that most managers would consider unacceptable.

On this, I’ll crib from Zed Shaw yet again, in Chapter 3 of Learn C the Hard Way:

In the Extra Credit section of each exercise I may have you go find information on your own and figure things out. This is an important part of being a self-sufficient programmer. If you constantly run to ask someone a question before trying to figure it out first then you never learn to solve problems independently. This leads to you never building confidence in your skills and always needing someone else around to do your work. The way you break this habit is to force yourself to try to answer your own questions first, and to confirm that your answer is right. You do this by trying to break things, experimenting with your possible answer, and doing your own research. (Emphasis mine.)

What Zed is describing here is the learning process that never occurs in the corporate environment, and the lack of it is one of the main reasons why corporate software engineers never improve. In the corporate world, you never find out why the build system is set up in the way that it is. You just go bug the person responsible for it. “My shit depends on your shit, so fix your shit so I can run my shit and my boss doesn’t give me shit over my shit not working for shit.” Corporate development often has to be this way, because learning a typical company’s incoherent in-house systems doesn’t provide a general education. When you’re studying the guts of Linux, you’re learning how a best-in-class product was built. There’s real learning in mucking about in small details. For a typically mediocre corporate environment that was built by engineers trying to appease their managers, one day at a time, the quality of the pieces is often so shoddy that not much is learned in truly comprehending them. It’s just a waste of time to deeply learn such systems. Instead, it’s best to get in, answer your question, and get out. Bugging someone is the most efficient and best way to solve the problem.

It should be clear that what I’m railing against is the commodity developer phenomenon. I wrote about “Java Shop Politics” last April, which covers a similar topic. I’m proud of that essay, but I was wrong to single out Java as opposed to, e.g. C#, VB, or even C++. Actually, I think any company that calls itself an “<X> Shop” for any language X is missing the point. The real evil isn’t Java the language, as limited as it may be, but Big Software and the culture thereof. The true enemy is the commodity developer culture, empowered by the modern bastardization of “object-oriented programming” that looks nothing like Alan Kay’s original vision.

In well-run software companies, programs are build to solve problems, and once the problem is finished, it’s Done. The program might be adapted in the future, and may require maintenance, but that’s not an assumption. There aren’t discussions about how much “headcount” to dedicate to ongoing maintenance after completion, because that would make no sense. If people need to modify or fix the program, they’ll do it. Programs solve well-defined problems, and then their authors move on to other things– no God Programs that accumulate requirements, but simple programs designed to do one thing and do it well. The programmer-to-program relationship must be one-to-many. Programmers write programs that do well-defined, comprehensible things well. They solve problems. Then they move on. This is a great way to build software “as needed”, and the only problem with this style of development is that the importance of small programs is hard to micromanage, so managerial dinosaurs who want to track efforts and “headcount” don’t like it much, because they can never figure out who to scream at when things don’t go their way. It’s hard to commoditize programmers when their individual contributions can only be tracked by their direct clients, and when people can silently be doing work of high importance (such as making small improvements to the efficiencies of core algorithms that reduce server costs). The alternative is to invert the programmer-to-program relationship: make it many-to-one. Then you have multiple programmers (now a commodity) working on Giant Programs that Do Everything. This is a terrible way to build software, but it’s also the one historically favored by IDE culture, because the sheer work of setting up a corporate development environment is enough that it can’t be done too often, and this leads managers to desire Giant Projects and a uniformity (such as a one-language policy, see again why “<X> Shops” suck) that managers like but that often makes no sense.

The right way of doing things– one programmer works on many small, self-contained programs– is the core of the so-called “Unix philosophy“. Big Programs, by contrast, invariably have undocumented communication protocols and consistency requirements whose violation leads not only to bugs, but to pernicious misunderstandings that muddle the original conceptual integrity of the system, resulting in spaghetti code and “mudballs”. The antidote is for single programs themselves to be small, for large problems to be solved with systems that are given the respect (such as attention to fault tolerance) that, as such, they deserve.

Are there successful exceptions to the Unix philosophy? Yes, there are, but they’re rare. One notable example is the database, because these systems often have very strong requirements (transactions, performance,  concurrency, durability, fault-tolerance) that cannot be as easily solved with small programs and organic growth alone. Some degree of top-down orchestration is required if you’re going to have a viable database, because databases have a lot of requirements that aren’t typical business cruft, but are actually critically important. Postgres, probably the best SQL out there, is not a simple beast. Indeed, databases violate one of the core tenets of the Unix philosophy– store data in plain text– and they do so for good reasons (storage usage). Databases also mandate that people be able to use them without having to keep up with the evolution of such a system’s opaque and highly-optimized internal details, which makes the separation of implementation from interface (something that object-oriented programming got right) a necessary virtue. Database connections, like file handles, should be objects (where “object” means “something that can be used with incomplete knowledge of its internals”.) So databases, in some ways, violate the Unix philosophy, and yet are still used by staunch adherents. (We admit that we’re wrong sometimes.) I will also remark that it has taken decades for some extremely intelligent (and very well-compensated) people to get databases right. Big Projects win when no small project or loose federation thereof will do the job.

My personal belief is that almost every software manager thinks he’s overseeing one of the exceptions: a Big System that (like Postgres) will grow to such importance that people will just swallow the complexity and use the thing, because it’s something that will one day be more important than Postgres or the Linux kernel. In almost all cases, they are wrong. Corporate software is an elephant graveyard of such over-ambitious systems. Exceptions to the Unix philosophy are extremely rare. Your ambitious corporate system is almost certainly not one of them. Furthermore, if most of your developers– or even a solid quarter of them– are commodity developers who can’t code outside of an IDE, you haven’t a chance.

Six languages to master.

Eric Raymond, in “How to Become a Hacker“, recommended five languages: C, Java, Python, Perl, and Lisp. Each he recommended for different reasons: Python and Java as introductory languages, C to understand and hack Unix, Perl because of its use in scripting, and Lisp for, to use his words which are so much better than anything I can come up with, the profound enlightenment experience you will have when you finally get it. That experience will make you a better programmer for the rest of your days, even if you never actually use LISP itself a lot.

It’s 2012. Many languages have come to the fore that didn’t exist when this essay was written. Others, like Perl, have faded somewhat. What is today’s five-language list? I won’t pretend that my answer is necessarily the best; it’s biased based on what I know. That said, I’d think the 5 highest-return languages for people who want to become good engineers are the following, and in this order: Python, C, ML, Clojure, and Scala.

Why these 5? Python I include because it’s easy to learn and, in-the-small, extremely legible. I’d rather not use it for a large system, but people who are just now learning to program are not going to be writing huge systems. They’re not going to be pushing major programs into production. At least, they shouldn’t be. What they should be able to do is scrape a webpage or build a game or investigate an applied math problem and say, “Shit, that’s cool.” Python gets people there quickly. That will motivate them to get deeper into programming. Python is also a language that is not great at many things, but good at one hell of a lot of them. It’s quick to write, legible in the small, and expressive. It allows imperative and functional styles. It has great libraries, and it has strong C bindings, for when performance is needed.

People who are getting started in programming want to do things that are macroscopically interesting from a beginner’s perspective. They don’t just want to learn about algorithms and how compilers work, because none of that’s interesting to them until they learn more of the computer science that motivates the understanding of why these things are important. Compilers aren’t interesting until you’ve written programs in compiled languages. At the start, people want to be able to write games, scrape webpages, and do simple systems tasks that come up. Python is good because it’s relatively easy to do most programming tasks in it.

After Python, C is a good next choice, and not because of its performance. That’s largely irrelevant to whether it will make someone a better programmer (although the confidence, with regard to understanding performance, that can come with knowing C is quite valuable). C is crucial because there’s a lot of computer science that becomes inaccessible if one sticks to higher-level languages (and virtual machines) like Java, C#, and Python. Garbage collection is great, but what is the garbage collector written in? C. As is Unix, notably. For all this, I think C is a better choice than C++ because there’s another important thing about C: C++ is a mess and it’s not clear whether it’s a good language for more than 1% of the purposes to which it’s put, but C, on the other hand, has utterly dominated the mid-level language category. For all its flaws, C is (like SQL for database query languages) a smashing, undisputed success, and for good reasons. The high-level language space is still unsettled, with no clear set of winners, but the mid-level language used to write the runtimes and garbage collectors of those high level languages is usually C, and will be for some time.

Python and C give a person coverage of the mid- and very-high levels of language abstraction. I’m avoiding including low-level (i.e. assembly) languages because I don’t think any of them have the generalist’s interest that would justify top-5 placement. Familiarity with assembly language and how it basically works is a must, but I don’t think mastery of x86 intricacies is necessary for most programmers.

Once a programmer’s fluent in Python and C, we’re talking about someone who can solve most coding problems, but improvement shouldn’t end there. Taste is extremely important, and it’s lack of taste rather than lack of intellectual ability that has created the abundance of terrible code in existence. Languages can’t inherently force people to learn taste, but a good starting point in this direction is ML: SML or OCaml with the “O” mostly not used.

ML has been described as a “functional C” for its elegance. It’s fast, and it’s a simple language, but its strong functional programming support makes it extremely powerful. It also forces people to program from the bottom up. Instead of creating vague “objects” that might be hacked into bloated nonsense over the lifespan of a codebase, they create datatypes (mostly, records and discriminated unions, with parameterized types available for polymorphism) out of simpler ones, and use referentially transparent functions as the basic building blocks of most of their programs. This bottom-up structure forces people to build programs on sound principles (rather than the vague, squishy abstractions of badly-written object-oriented code) but ML’s high-level capability brings people into the awareness that one can write complex software using a bottom-up philosophy. Python and C teach computer science at higher and lower levels, but ML forces a programmer to learn how to write good code.

There’s also something philosophical that Python, C, and Ocaml tend to share that C++ and Java don’t: small-program philosophy, which is generally superior. I’ve written at length about the perils of the alternative. In these languages, it’s much more uncommon to drag in the rats’ nest of dependencies associated with large Java projects. For an added bonus, you never have to look at those fucking ugly singleton directories called “com”. Once a person has used these three languages to a significant extent, one gets a strong sense of how small-program development works and why immodular, large-project orientation is generally a bad thing.

When you write C or Ocaml or Python, you get used to writing whole programs that accomplish something. There’s a problem, you solve it, and you’re done. Now you have a script, or a library, or a long-running executable. You may come back to it to improve it, but in general, you move on to something else, while the solution you’ve created adds to the total stored value of your code repository. That’s what’s great about small-program development: problems are actually solved and work is actually “done” rather than recursively leading to more work without any introspection on whether the features being piled on the work queue make sense. Developers who only experience large-program development– working on huge, immodular Java projects in IDEs a million metaphorical miles from where the code actually runs for real– never get this experience of actually finishing a whole program.

Once a person has grasped ML, we’re talking about a seriously capable programmer, even though ML isn’t a complex language. Learned in the middle of one’s ML career is a point to which I’ll return soon, but for now leave hanging: types are interesting. One of the most important things to learn from ML is how to use the type system to enforce program correctness: it generates a massive suite of implicit unit tests that (a) never have to be written, and (b) don’t contribute to codebase size. (Any decent programmer knows that “lines of code” represent expenditure, not accomplishment.)

The fourth language to learn is Clojure, a Lisp that happens to run on the JVM. The JVM has its warts, but it’s powerful and there are a lot of good reasons to learn that ecosystem, and Clojure’s a great entry point. A lot of exciting work is being done in the JVM ecosystem, and languages like Clojure and Scala keep some excellent programmers interested in it. Clojure is an excellent Lisp, but with its interactive “repl” (read-eval-print-loop) and extremely strong expressivity, it is (ironically)  arguably the best way to learn Java. It has an outstanding community, a strong feature set, and some excellent code in the open-source world.

Lisp is also of strong fundamental importance, because its macro system is unlike anything else in any other language and will fundamentally alter how an engineer thinks about software, and because Lisp encourages people to use a very expressive style. It’s also an extremely productive language: large amounts of functionality can be delivered in a small amount of time. Lisp is a great language for learning the fundamentals of computing, and that’s one reason why Scheme has been traditionally used in education. (However, I’d probably advocate starting with Python because it’s easier to get to “real stuff” quickly in it. Structure and Interpretation of Computer Programs and Scheme should be presented when people know they’re actually interested in computing itself.)

When one’s writing large systems, Lisp isn’t the best choice, because interfaces matter at that point, and there’s a danger that people will play fast-and-loose with interfaces (passing nested maps and lists and expecting the other side to understand the encoding) in a way that can be toxic. Lisp is great if you trust the developers working on the project, but (sadly) I don’t think many companies remain in such a state as they grow to scale.

Also, static typing is a feature, not a drawback. Used correctly, static typing can make code more clear (by specifying interfaces) and more robust, in addition to the faster performance usually available in compiled, statically typed languages. ML and Haskell (which I didn’t list, but it’s a great language in its own right) can teach a person how to use static typing well.

So after Lisp, the 5th language to master is Scala. Why Scala, after learning all those others, and having more than enough tools to program in interesting ways? First of all, it has an incredible amount of depth in its type system, which attempts to unify the philosophies of ML and Java and (in my opinion) does a damn impressive job. The first half of Types and Programming Languages is, roughly speaking, the theoretic substrate for ML. But ML doesn’t have a lot of the finer features. It doesn’t have subtyping, for example. Also, the uniqueness constraint on record and discriminated union labels (necessary for full Hindley-Milner inference, but still painful) can have a negative effect on the way people write code. The second half of TAPL, which vanilla ML doesn’t really support, is realized in Scala. Second, I think Scala is the language that will salvage the 5 percent of object-oriented programming that is actually useful and interesting, while providing such powerful functional features that the remaining 95% can be sloughed away. The salvage project in which a generation of elite programmers selects what works from a variety of programming styles– functional, object-oriented, actor-driven, imperative– and discards what doesn’t work, is going to happen in Scala. So this is a great opportunity to see first-hand what works in language design and what doesn’t.

Scala’s a great language that also requires taste and care, because it’s so powerful. I don’t agree with the detractors who claim it’s at risk of turning into C++, but it definitely provides enough rope for a person to hang himself by the monads.

What’s most impressive about Clojure and Scala is their communities. An enormous amount of innovation, not only in libraries but also in language design, is coming out of these two languages. There is a slight danger of Java-culture creep in them, and Scala best practices (expected by the leading build environments) do, to my chagrin, involve directories called “src” and “main” and even seem to encourage singleton directories called “com”, but I’m willing to call this a superficial loss and, otherwise, the right side seems to be winning. There’s an incredible amount of innovation happening in these two languages that have now absorbed the bulk of the top Java developers.

Now… I mentioned “six languages” in this post’s title but named five. The sixth is one that very few programmers are willing to use in source code: English. (Or, I should say, the scientifically favored natural language of one’s locale.) Specifically, technical English, which requires rigor as well as clarity and taste. Written communication. This is more important than all of the others. By far. For that, I’m not complaining that software engineers are bad at writing. Competence is not a problem. Anyone smart enough to learn C++ or the finer points of Lisp is more than intelligent enough to communicate in a reasonable way. I’m not asking people to write prose that would make Faulkner cry; I’m asking them to explain the technical assets they’ve created at, at the least, the level of depth and rigor expected in a B+ undergraduate paper. The lack of writing in software isn’t an issue of capability, though, but of laziness.

Here’s one you hear sometimes: “The code is self-documenting.” Bullshit. It’s great when code can be self-documenting, making comments unnecessary, but it’s pretty damn rare to be solving a problem so simple that the code responsible for solving it is actually self-explanatory. Most problems are custom problems that require documentation of what is being solved, why, and how. People need to know, when they read code, what they’re looking at; otherwise, they’re going to waste a massive amount of time focusing on details that aren’t relevant. Documentation should not be made a crutch– you should also do the other important things like avoiding long functions and huge classes– but it is essential to write about what you’re doing. People need to stop thinking about software as machinery that “explains itself” and start thinking of it as writing a paper, with instructions for humans about what is happening alongside the code actually doing the work.

One of the biggest errors I encounter with regard to commenting is the tendency to comment minutiae while ignoring the big picture. There might be a 900-line program with a couple comments saying, “I’m doing this it’s O(n) instead of O(n^2)” or “TODO: remove hard-coded filename”, but nothing that actually explains why these 900 lines of code exist. Who does that help? These types of comments are useless to people who don’t understand what’s happening at all, which they generally won’t in the face of inadequate documentation. Code is much easier to read when one knows what one is looking at, and microcomments on tiny details that seemed important when the code was written are not helpful.

Comments are like static typing: under-regarded if not ill-appreciated because so few people use them properly, but very powerful (if used with taste) in making code and systems actually legible and reusable. Most real-world code, unfortunately, isn’t this way. My experience is that about 5 to 10 percent of code in a typical codebase is legible, and quite possibly only 1 percent is enjoyable to read (which good code truly is). The purpose of a comment should not be only to explain minutiae or justify weird-looking code. Comments should also ensure that people always know what they’re actually looking at.

The fallacy that leads to a lack of respect for documentation is that writing code is like building a car or some other well-understood mechanical system. Cars don’t come with a bunch of labels on all the pieces, because cars are fairly similar under the hood and a decent mechanic can figure out what is what. With software, it’s different. Software exists to solve a new problem; if it were solving an old problem, old software could be used. Thus, no two software solutions are going to be the same. In fact, programs tend to be radically different from one another. Software needs to be documented because every software project is inherently different, at least in some respects, from all the others.

There’s another problem, and it’s deep. The 1990s saw an effort, starting with Microsoft’s visual studio, to commoditize programmers. The vision was that, instead of programming being a province of highly-paid, elite specialists with a history of not working well with authority, software could be built by bolting together huge teams of mediocre, “commodity” developers, and directing them using traditional (i.e. pre-Cambrian) management techniques. This has begun to fail, but not before hijacking object-oriented programming, turning Java’s culture poisonous, and creating some of the most horrendous spaghetti code (MudballVisitorFactoryFactory) the world has ever seen. Incidentally, Microsoft is now doing a penance by having its elite research division investigate functional programming in a major way, the results being F# and a much-improved C#. Microsoft, on the whole, may be doomed to mediocrity, but they clearly have a research division that “gets it” in an impressive way. Still, that strikes me as too little, too late. The damage has be done, and the legacy of the commodity-developer apocalypse still sticks around.

The result of the commodity-programmer world is the write-only code culture that is the major flaw of siloized, large-program development. That, I think, is the fundamental problem with Java-the-culture, IDE-reliance, and the general lack of curiosity observed (and encouraged) among the bottom 80 percent of programmers. To improve as programmers, people need to read code and understand it, in order to get a sense of what good and bad code even are, but almost no one actually reads code anymore. IDEs take care of that. I’m not going to bash IDEs too hard, because they’re pretty much essential if you’re going to read a typical Java codebase, but IDE culture is, on the whole, a major fail that makes borderline-employable programmers out of people who never should have gotten in in the first place.

Another problem with IDE culture is that the environment becomes extremely high maintenance, between plugins that often don’t work well together, build system idiosyncracies that accumulate over time, and the various menu-navigation chores necessary to keep the environment sane (as opposed to command-line chores, which are easily automated). Yes, IDEs do the job: bad code becomes navigable, and commodity developers (who are terrified of the command line and would prefer not to know what “build systems” or “version control” even are) can crank out a few thousand lines of code per year. However, the high-maintenance environment requires a lot of setup work, and I think this is culturally poisonous. Why? For a contrast, in the command-line world, you solve your own problems. You figure out how to download software (at the command line using wget, not clicking a button) and install it. Maybe it takes a day to figure out how to set up your environment, but once you’ve suffered through this, you actually know a few things (and you usually learn cool things orthogonal to the problem you were originally trying to solve). When a task gets repetitive, you figure out how to automate it. You write a script. That’s great. People actually learn about the systems they’re using. On the other hand, in IDE-culture, you don’t solve your own problems because you can’t, because there it would take too long. In the big-program world, software too complex for people to solve their own problems is allowed to exist. Instead of figuring it out on your own, you flag someone down who understands the damn thing, or you take a screenshot of the indecipherable error box that popped up and send it to your support team. This is probably economically efficient from a corporate perspective, but it doesn’t help people become better programmers over time.

IDE culture also creates a class of programmers who don’t work with technology outside of the office– the archetypal “5:01 developers”– because they get the idea that writing code requires an IDE (worse yet, an IDE tuned exactly in line with the customs of their work environment). If you’re IDE-dependent, you can’t write code outside of a corporate environment, because when you go home, you don’t have a huge support team to set the damn thing up in a way that you’re used to and fix things when the 22 plugins and dependencies that you’ve installed interact badly.

There are a lot of things wrong with IDE culture, and I’ve only scratched the surface, but the enabling of write-only code creation is a major sticking point. I won’t pretend that bad code began with IDEs because that’s almost certainly not true. I will say that the software industry is in a vicious cycle, which the commodity-developer initiative exacerbated. Because most codebases are terrible, people don’t read them. Because “no one reads code anymore”, the bulk of engineers never get better, and continue to write bad code.

Software has gone through a few phases of what it means for code to actually be “turned in” as acceptable work. Phase 1 is when a company decides that it’s no longer acceptable to horde personal codebases (that might not even be backed up!) and mandates that people check their work into version control. Thankfully, almost all companies have reached that stage of development. Version control is no longer seen as “subversive” by typical corporate upper management. It’s now typical. The second is when a company mandates that code have unit tests before it can be relied upon, and that a coding project isn’t done until it has tests. Companies are reaching this conclusion. The third milestone for code-civilizational development, which very few companies have reached, is that the code isn’t done until you’ve taught users how to use it (and how to interact with it, i.e. instantiate the program and run it or send messages to it, in a read-eval-print-loop appropriate to the language). That teaching can be supplied at a higher level in wikis, codelabs, and courses… but it also needs to be included with the source code. Otherwise, it’s code out-of-context, which becomes illegible after a hundred thousand lines or so. Even if the code is otherwise good, out-of-context code without clear entry-points and big-picture documentation becomes incomprehensible around this point.

What do I not recommend? There’s no language that I’d say is categorically not worth learning, but I do not recommend becoming immersed in Java (except well enough to understand the innards of Clojure and Scala). The language is inexpressive, but the problem isn’t the language, and in fact I’d say that it’s unambiguously a good thing for an engineer to learn how the JVM works. It’s that Java-the-Culture (VisitorSelectionFactories, pointless premature abstraction, singleton directories called “com” that betray dripping contempt for the command line and the Unix philosophy, and build environments so borked that it’s impossible not to rely on an IDE) that is the problem; it’s so toxic that it reduces an engineer’s IQ by 2 points per month.

For each of these five programming languages, I’d say that a year of exposure is ideal and probably, for getting a generalist knowledge, enough– although it takes more than a year to actually master any of these. Use and improvement of written communication, on the other hand, deserves more. That’s a lifelong process, and far too important for a person not to start early on. Learning new programming languages and, through this, new ways of solving problems, is important; but the ability to communicate what problem one has solved is paramount.