Hacker News new | comments | ask | show | jobs | submit login
Frequent versus infrequent developers, in languages and so on (utoronto.ca)
176 points by simula67 10 months ago | hide | past | web | favorite | 62 comments

The thing that always get me when doing infrequent development is the differences in how languages handle common tasks.

For instance, if I have a string and need to get its length. Do I call a length() function passing the string? Or is a string an object and there is a .length() member function? Or maybe its an object but the length is a property?

Not a super big deal...but it is an annoying interruption when I realize I don't remember which this particular language uses, and have to either go Googling or start looking around the code to try to find an example. There are a whole bunch of these.

Is it "elseif", "elif", "elsif", "else if", or sometime else?

Is the "for" loop like C, or does it take some kind of iterator object? Is it "for" or "foreach"?

Null, nil, undef?

How to break an outer loop from within an inner loop? How to continue an outer loop from within an inner loop?

Variable scope is a whole can of worms. In some to access a global from inside a function you have to explicitly say you want the global. In others variables in functions are global unless explicitly marked otherwise. Is the scope of a variable declared in a nested block just its block, or the whole function? Is it lexically scoped?

(The scope ones are particularly treacherous, because getting those wrong often won't cause a compile or run-time error. You might just end up creating a global when you thought you were creating a local, or changing a local when you thought you were changing a global).

How do I do multi-line string literals? How do I concatenate strings? Parse strings as numbers? Apply regular expressions to strings? Find substrings?

I think these problems go even deeper. Languages have patterns which are used at the architectural level, too, and an infrequent developer will miss those patterns.

A good example of this is generators in Python. Generators have become pervasive in Python 3, but infrequent users of Python will often prefer lists to generators. This will result in pretty large memory and performance overheads in rare cases, but more often it just results in inconsistent code.

At the most extreme, infrequent users can have such an effect that they affect the language. JavaScript has had a prototypical object model for over a decade, but ES6 adds classes because too many infrequent JavaScript developers from other languages couldn't be arsed to understand prototypes. The result is that now there are two object models which are incompatible at the edges (yes, classes are technically implemented on top of prototypes, but the pattern the class expands to is an anti-pattern in a prototypical object flow).

Classic polyglot problems :)

I really, really resent how every language seems to use the same concept but slightly different wording. The elif/elsif/'else if' distinction is a particularly annoying one. Same for null/None/nil

And camelCase or not to camelcase:

    /* OR */

I'm also an infrequent developer in many languages. I find this is a really important article especially for new languages (Go, Rust, F#).

If your language has less adoption most of your developers will be infrequent developers. There won't be many companies purely building on this one technology. You will have a lot of people dabbling in small side projects to see whether the language fits their needs.

So it's important to have an easy way to update your whole toolchain or install the toolchain easily whenever needed. E.g. updating my Rust toolchain means runnning rustup update and updating VSCode (the RLS extension updates automatically) cargo does everything automatically for builds with a very simple project structure.

It's also best if you can avoid the need for specific tooling (I use VSCode as my main editor anyway).

Another thing that should be thought of is that commands should do similar things like in other languages in order to not surprise people unnecessarily. If you want to go against the flow, you should have a good reason for it.

I sometimes wonder if the tooling should be different for infrequent tasks. I might update the build scripts for my desktop software just once or twice a year, given the infrequent use it would make sense to use a GUI for this rather than a text editor.

Indeed - infrequent tasks should be low-friction, or infrequent turns into "never".

I'd say low friction tooling even helps a famous language. It's essential for convincing the dabbler though.

This was definitely a problem for me with F#. It took me a solid 4 hours to figure out that the best way for me to build the tiny program I was working on was just to run fsharpc from the command line. It seemed like there were a number of quite complicated ways to structure F# projects, but I couldn't figure out what problems they were trying to solve.

I was using it mostly with VSCode and fs(harp)i/fs(harp)c as well, because I didn't have Visual Studio Professional (you can't use community at work) on my company computer.

With VS it's quite easy to manage projects. The cool thing about Rust is that the Cargo system works well from a simple text editor. And with cargo init you get an empty project set up.

If you infrequently use a language and you are not planning on writing write-once/throw-away code. Then don't use any library/framework. The overhead of development today as I see it is not just the languages, but the cognitive overhead of having to learn gigantic libraries and frameworks. I'm seeing libraries/frameworks that are 20x-100x times as complex as the language. Shed those bloat and free your mind. If the language is also fairly large, constraint yourself and restrict yourself to a subset of the language. It's not a must that one must use every feature of a language. The suffering of developers that use an infrequent language is self inflicted. Hell, don't use a language that you don't plan to use often unless it truly offers something that other languages don't, which is rarely so.

I would generalize this for accelerated learning: Identify the minimum you need to learn to complete your next task and learn only that. This is difficult for two reasons. First, without the knowledge it's hard to identify what is the minimum you need to learn. And second, as someone with an interest in technology it's hard to stop yourself from going deeper. Solve the first problem by guessing with a bias towards learning less and solve the second problem with discipline.

If you proceed with this accelerated learning process you can start knocking out tasks using new technology rather fast and you will achieve better results. You may not know all the details of every language and tool that you use but you'll know how to get things done.

I also recommend avoiding unnecessary libs/frameworks for any code that you expect to run for a long time without change.

I do this at work when writing small services in Go and it works out well. Some have been running for three years now without being touched yet there is no code rot and anyone can easily pick it up.

Absolutely this. Even the base dev environment is often an exercise in madness. I loathe every new iOS version because Xcode will break my code in new and interesting ways, requiring me to recheck every single library I've developed to make sure they still compile (often they don't) and run (almost always they don't).

And then, once I've got that headache handled, I update the version number, type 'pod lib lint', and watch all the new and interesting ways that cocoapods has broken things since I last used it.

Every 3rd party component you rely on is something that WILL break as soon as you need to rebuild your project.

Not necessarily. People make fun of Java, but any half decent Java place, especially the ones using Maven, won't have this problem.

Maven is quite verbose and it forces you to pin a lot of things down. Where it doesn't outright force you, the general practices are to pin them regardless.

All the dependencies are in the same immutable Maven repo (or your own proxy/cache), all the build tool extensions/plugins/etc. are there as well. Java is generally very good at backwards compatibility, as is Maven itself.

So you can come back a few years later and do a :

mvn clean install

and things will work.

Of course, humans are fallible and things could still break due to various factors, but in this sense the ecosystem helps you and either offers stability already bundled it or guides you towards achieving it.

The downside: Maven is boring. Developers don't like boring :)

In JS world we got a package lockfile fairly recently. Locking your own package.json version wasn't enough because packages have deps too.

Beforehand running npm install on newly checked out repos was a gamble, especially given that we have packages for every minute "feature" like leftpad (which is a horror story of its own).

I absolute agree. The only reason I didn't really mention this is that I don't want to push for the "not invented here" behavior. Once in a while, there are times when some library/frameworks are worth it. More often than not that's not the case.

The thing I don't like on this front for Go is the whole "dependencies on Github" thing.

If the dependencies are not vendored or pointing to the correct thing (tags, always tags!), 3 years from now your project might not build.

I like the Maven approach of the immutable binary repo.

One day a long, long time ago, I found a broken dependency from IBM uploaded on the Maven Central repo, I think. I sent the Maven Central repo maintainers a mail saying that the dependency was broken. They replied saying that they don't ever touch uploaded releases. At the time I was kind of pissed off cause it was a transitive dependency that was breaking stuff. I had to hack around it.

But over the years I've come to appreciate that wisdom.

Contrast: npm.

> The thing I don't like on this front for Go is the whole "dependencies on Github" thing.

Agreed. I really dislike the GOPATH concept that the original tooling is built around, but I strictly make use of vendoring.


Even as you get deeper into a particular domain, always ask "What is the return?" on investing in any extra toolset.

If the cost and mental overhead of the bigger toolset will provide solid improvements in process efficiency and/or quality, then by all means, go for it. But if not...

This also applies to other domains, including design & manufacturing, which I'm in now. Sometimes the best, most expensive tool is worth 10x what it costs, and sometimes it's better to just use the basic tools.

Caveat emptor, and even more for your time than your money

Sometimes though I think it can't be helped. I for one find myself in charge of maintaining a lot of code that I didn't originally write. For the things that need to be updated and maintained the most I find myself owning and becoming more of a frequent developer of it, but there are still a lot of things that I need to fix if some issue arises, but I have not touched in years if not ever. I can see why the original author wrote it the way they did--they are most likely a frequent developer in that language and in those patterns--but as someone stepping in, I can feel the pain of infrequent developers in that case.

As an "infrequent developer", I usually piggyback on others to find best practices, in case I have a bigger project. For the tiny ones it simply doesn't matter.

The advantages of being an "infrequent developer" is that you grow an intuition on how to get started with something completely new (cfr. Taleb's anti-fragile), and that you learn a lot from cross-contamination.

Also, others might refer to frequent/infrequent developers as specialists and generalists.

(Edit: reformulation of some Dunglish sentences)

I don't think that the "infrequent developers" they are talking about are necessarily generalists.

For example, you may be an expert in Python and need to write a bit of C code for now and then. It makes you a frequent Python dev and an infrequent C dev.

In fact, good generalists should be able to fit the "frequent developer" pattern more closely for any language, at a reduced efficiency of course. "infrequent developers" are more likely to be specialists that are out of their specialty.

I find this issue with Node.js stuff. I can't count the times where I found some software that looks great, would really help development, but then it's based off of Node (which we don't use at all), and it requires 3 build tools (none of which we use) and a pile of dependencies... :(

I think node of all things have no excuses. They're javascript all the way down, why don't they have one command to download, build and deploy everything, including any build tools that come to need? Instead they seem to have a baroque setup

Well maintained packages do have that:

    npm install
    npm run (dev|build|deploy)
Poorly maintained ones don't have these run command set and instead rely on pages upon pages of crap in README.md or do have a subset hacked together from whatever the dev needed for their FUD-driven development workflow.

Edit: as an afterthought: maybe the infrequent developers are to blame for not reading about what npm provides?

Edit2: another, I guess inexperienced, developer type I've seen in the wild is "I did some jQuery 5y ago, React probably is as easy to pick up and spaghetti out". For these: stick to what you know and don't play the hype game.

I doubt any of that is specific to infrequent developers.

I agree. I think this is good advice in general.

I suggest one looks at the standard library and ask for people's opinion on well written libraries to pick the style, idioms, limitations and tricks of a language (and even compiler at points depending on what you do -- representations of built-in data structures are enlightening in general).

Web Development in recent years has made being an infrequent Web developer unnecessarily complex, to the point that only frequent developers can perform tasks

Absolutely, I tend to stay away from web development, but when I have to do it it's just seems so unecessarily complex and painful.

Only if you are using the latest hipster JS framework. I write only vanilla JS (infrequently) and things have been getting easier and easier as browsers (and JS) advance.

Same approach here.

Server side rendering with Java and .NET web stacks, with vanila JS.

jQuery seems to be an underrated gem. It's just as good as 10 years ago.

It's not that it is underrated, the paradigm has moved past manually grabbing bits of the DOM and updating them. Today, it is all about two way databinding. You have a variable that lives in your controller and when he is updated so is the DOM, automatically. I'm glad jQuery has been put to pasture because you end up writing way less code which is way more maintainable.

What's a good field for an infrequent developer to become a frequent one? It seems all web dev companies expect even people applying for junior positions to be frequent developers (e.g. practiced at churning out REST APIs)

I'd like to make explicit what the author made only implicit, that languages are just one example of many. For me, setting up an apache server used to be a task that took 2 hours, even when I was an engineer with half a decade of experience (mostly windows).

I view this as a major failure. In fact, if your open source software takes many hours to set up, it's costing hundreds of dollars of effort to learn the unnecessary quirks every time.

(Example issues: permissions, conf file failing silently, server installing in multiple distinct places leading to multiple installations, .htaccess ignored due to config setting, config settings including directories of other config settings, logs going to different places, OS security setting screwing up server. )

I like to defend apache a little bit here since you can actually do a lot with no config file or even a very small one. :)

It's your crazy (I mean really crazy on debian/ubuntu) distro who made it so scary.


> In fact, if your open source software takes many hours to set up, it's costing hundreds of dollars of effort to learn the unnecessary quirks every time.

You can see it as supporting an ecosystem of packagers and integrators instead. Remember it's "only free if your time is worth nothing".

The config mess of Apache gives me traumatic memories. There always exists some other config file or setting parameter which is loaded instead of the one I want.

Some cool insights in there that I have not had the ability to articulate myself but I have been feeling for maybe 5 or so years.

The biggest hurtle is that a "frequent dev" isn't always the best at remembering the sum of everything needed and so it takes an "infrequent dev" to write the docs and outline the helper tools and environment setup and common gotchas.

Which if done for a certain amount of time the "infrequent dev" will gain enough insight and mastery until they move into the "frequent dev" category. As a result they will become blind to the pitfalls. They become the blind "expert".

This is why I'm completely fascinated with teaching and knowledge transfer and I feel like there is huge advances that can be done in this field.

Here is the catcher, this isn't just limited to computers or programming! In fact it isn't even limited to spoken language.

This "blindness" to detail applies to all of reality!

If you want to read more checkout: http://johnsalvatier.org/blog/2017/reality-has-a-surprising-...

> As a result [the now frequent developer] will become blind to the pitfalls. They become the blind "expert".

This is known as “The Curse of Knowledge”:


Another major source of frustration for infrequent developers is tools & commands that cater to the 10% first and the 90% second (commands that require a bunch of flags/configuration to be set for the 90% use case).

If your tools/frameworks don't do the 90% use case by default, you're doing it wrong.

I was trying to be an infrequent user of OpenGL. People were totally not understanding why amn't I "just writing a shader".

Modern 3D environment is hostile to infrequent developers now that drawing a simple sphere suddently needs a page length blob of code. Or a "model". Exported from "3D editor".

I agree that graphics programming has become more and more hostile to infrequent users over the years. Comparing the setup boilerplate needed to draw a triangle from OpenGL to Vulkan is just horrifying.

I frequently imagine the terror that junior computer graphics devs encounter when they realize you can't just "draw lines" in OpenGL.

...and there's no good reason why you can't, other than that in some iteration of API, frequent developers could not imagine why would anybody need that.

Isn't it because everything is represented as triangles? That is fundamental to how openGL works I believe.

Everything can be represented in terms of everything else. It's a matter of caring, or not caring, about convenience.

Oh dear lord yes. I was struggling to just plot some points on a plane 15 years ago, something I'd done trivially in class years prior. I finally complained to a friend, and that's how I learned about python and pygame. Damn near changed my life.

Go is actually a pretty good language for infrequent developers. There isn't much to is so it's easy to pick it up again. The language is largely backward-compatible so it's easy to pick the latest version and get up and compiling. It also encourages low levels of abstractions which in my mind is the biggest scourge against infrequent programmers. Now if only dependency management was solved...

I agree that Go makes infrequent use easy. That was part of why I went for python over perl; I just didn't use perl often enough for it to stick in my head. There are similar issues with Haskell, just because of all the many libraries of abstractions and operators to memorize.

I still don't see why dependency management has everyone so up in arms for Go, though. Especially for infrequent use, it's hard to beat just copying the lib into your source tree and re-fetching from git every now and then.

> I still don't see why dependency management has everyone so up in arms for Go, though. Especially for infrequent use, it's hard to beat just copying the lib into your source tree and re-fetching from git every now and then.

That's not "dependency management", that's step 1 of any development environment setup ever made. It's basically giving up on dependency management and doing everything manually.

The correct way to do dependency management:

Have a config file with all the dependencies, clearly specified, including versions.

Have a tool that has a standard command (mvn install, gradle build, npm install, etc.) without parameters that brings the dependencies locally and makes them available for your local environment.

For the standard command, depending on your environment and philosophy, either fetch sources or fetch sources and compile them or fetch binaries.

To upgrade a library, change the version in the config file and re-run the tool. To add a library, add the library info in the config file and re-run the tool. To remote the library, remove the library info from the config file.

The cognitive overhead is much, much smaller with a well designed dependency management setup. I just run the standard command and it does what I need, no rummaging through Github repos to find what I need.

Me, I long for the days when I could just download a tarball and be done. :-)

Seriously, as a mostly C++/python/R developer, maven was a PITA to set up for experimenting with Java, and I still don't feel like I entirely understand the npm/bower/yarn ecosystem. (Will they install globally or locally? What permissions do I need? etc. etc.)

It's certainly not something that I can't learn, or something that's really all that complex, but it's another barrier to just messing around with a new project. It's better for the frequent-users than the infrequent.

As an infrequent user of Java/Javascript, I'm fine with just downloading a tarball or cloning a single all-inclusive repo, and dealing with it being harder to update or to push a change back to the source project.

Even in python, I just maintain a local global install and occasionally update numpy/scipy manually. I'm not saying it's the best setup, but it's pretty easy to download verson X.Y.Z, config/make/make install, and done.

I imagine the pain is setting the environment variables? This thing: https://maven.apache.org/install.html

After that you run mvn install and things should just work. If not, the project maintainers have bigger issues :)

If only I could write code without having memories of using Borland C++ 2.0 macro based containers or Turbo Vision containers for Turbo Pascal on MS-DOS....

I don't find that this hits home.

Instead, I find that nobody on planet Earth is as disrespectful of your time as programmers.

Imagine the slowest waiting room for some government office. You might be stuck sitting there for 5 hours.

To a programmer, that's nothing! You'll be up and running the same afternoon.

They routinely send you off to RTFM, and if it will only take you two or three days that's nothing. It's like programmers live in a magical land where time is free, and they just don't understand why it's an issue for anyone.

It's about time as a resource.

That's a very black and white point of view. There's lots of grey areas and varying urgencies for problems. I'm sure most respectable programmers would take care of matters as fast as they could, even if it isn't that urgent.

If it takes me an hour to take systems back up after things go bad during the after-hours, it's not because I was purposely not doing my work. It's because that's literally what took me to fix the problem.

Programmers aren't magicians and we certainly aren't out there to get you and waste your time on purpose.



>Programmers aren't magicians

All right, so this part is tangential but after I posted my (GP) comment I realized that in one sense programmers are magicians. I had left the following aspect out of my black-and-white comment:

(I hadn't considered that) some of the best programmers I know don't REALLY read every word, the way you read my comment or I read yours. There's just no way there's enough time, given the speed of use of totally new information in totally new documentation that I've seen. It would be inhuman if they really read every word.

I think they must skim and scroll and use magical ways of picking out just what they need.

So when someone links a 500-page specification and tells you to "read it" (which might take, say, two weeks of an hour a day to read, starting at the first word and ending on the last -- perhaps they legitimately expect you to "read" it in 1 minute 48 seconds, since that's how long it takes to "read" it.

Back on topic


You've directed most of your response to how long it takes to do something. But I had actually directed my comment at how long, when designing (documentation/installation/whatever) you expect people to spend doing something.

With the tangent I just added, I realize that perhaps some of it is misleading. If you direct someone to do something that takes 15 hours, that doesn't mean you don't expect them to be done within 3-10 minutes. Because programmers have magical abilities at times.

So it is far from as certain as I had said.

Elixir got a lot right with regards to the infrequent developers -- the tooling and the package management is very nice and low-friction.

Even for non-infrequent developers, an automated dev environment set up is welcome.

As an infrequent developer, dabbler, and amateur in a vast number of programming languages, I think a great deal of the difficulties that arise when plodding around between different languages stem from a great number of design decisions that prioritize elegance, abstraction, terseness, and library economy over user experience. What results are a number of extremely powerful language features that are useful to everyone but remain inaccessible to anyone uninitiated into the cabal of that language's practitioners.

While we're on the subject of cabals, I think Haskell is a paragon case of this type of situation. Haskell is a phenomenal language and has the potential to be incredibly expressive and useful when it comes to accomplishing tasks that require one and done programmatic solutions. However, the language ecosystem refuses to abandon a great number of practices that, while they are historically and conceptually justified given its relation to category theory and mathematics, will ensure haskell never sees the same degree of general purpose adoption other languages do. For example, the following function is one I came up with on the spot for illustrative purposes:

folds :: (b->b->b)->[b]->[b]

folds f x = map (\n -> foldl f n x) x

It's not immediately obvious what this does unless you have basic knowledge of functional programming. Part of the difficultly stems from my choice of names, but I'm sticking with the convention to use single letters in the majority of cases that capture abstractions.

If I have obtained the prerequisite knowledge of haskell necessary to quickly read the above simple function and tell what it does it's incredibly powerful and easy reading, however, if I'm just picking things up for a small project there is waaaaaay to much learning overhead for me to even consider using haskell. Compare the haskell function with the same function expressed in javascript:

function folds(f, items){ return items.map(x => items.reduce(f, item)); }

If you are familiar with haskell, the haskell version is probably faster to read and comprehend. However, if you don't know haskell the js is probably easier to come to grips with based on its lingual contents.

The there's common lisp, which almost reads like English if it were't drowning in parens and quote weirdness:

(defun folds (f items) (map 'list (lambda (item) (reduce f items :initial-value item)) items))

The keyword arg goes a long way in helping the code read like a sentence, which is a good thing. In fact, that's the sort of syntactic support of semantics swift promotes using parameter labels:

func move(from start: Point, to end: Point)

x.move(from: x, to: y)

It's immediately clear to a caller what purpose each of the arguments of that function serve.

Obviously this is a huge, complex issue, and I'm never going to do it justice in a hackernews comment, but in general I think if library designers and developers focused more on user experience and usability we'd have more pleasant code to interact with. User experience isn't a concept reserved strictly for graphical applications--any tool, no matter what it is, has a user experience, and it's important to consider the user up front when we go about designing technology.

In this respect I think Knuth's concept of literate programming has a lot of worth, as it can cut down on research time tremendously and increase ease of use--if the code itself can provide all of the contextual information I need, there's no reason for me to go scouring the documentation, which--another issue--is frequently spotty, unclear or confusing, and on some occasions incomplete.

I wish this had more upvotes. I agree. That's one of the reasons Python is so popular, despite its many flaws. It's just easier to grok for newcomers. And overall it is quite consistent and usable.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact