1. 52
  1. 22

    Hello, I’m a niche programmer for over 10 years!

    Your salaries are double and in a lot of ways the job is easier. You’ll definitely get interviews and most likely job offers. It is a lot easier to be on interview with someone you sort-of know through Slack who at least knows you at a technical level.

    However, you’re now restrained in your career. You’re the only one who can develop in a certain framework/language/stack? They have no incentive to promote you, which might actually be a positive. Niches are behind the mainstream by at least five years which can be incredibly frustrating. You constantly feel that you’re at a dead-end, that at anytime your salary and the cost of maintaining a niche will exceed simply moving to a mainstream platform. You’re never doing anything revolutionary or cutting edge and when you do run into odd bugs or hard problems there’s often a solution just out of reach and because your niche hasn’t adopted the practical modern solution, so you band-aid it.

    Similarly transitioning to a mainstream environment is hard. It seems like the wild west of importing random packages, loose CI, simply amateur fast and loose type programming. The discipline you gained from being in a niche and knowing that if you didn’t document everything, check every edge case, you’d be back to debugging a compiler … well that doesn’t exist. Like an old car where you could tell what was wrong with it by the sound from a mile away, you leave your warm blanket of the niche … of knowing everything to realizing people have no idea how certain things work and frankly it doesn’t matter. There’s a certain paralysis that seeps in where you feel you must know the best way to do it rather than just slap things together.

    And simply changing from a world where you know everyone to the mainstream where recruiters won’t go, “You’re hard to find!” and having to justify your experience is frustrating. I think the best is to get a niche you can fall back on but don’t fall into.

    1. 18

      Not all niches are legacy niches, though.

      1. 3

        You’re never doing anything revolutionary or cutting edge …

        … because your niche hasn’t adopted the practical modern solution …

        I feel it’s a bit different with Clojure. It’s a hosted language. You need to know the host platform. At least to a certain degree. And I constantly see fascinating solutions that look highly practical from the Clojurist’s viewpoint. Some solutions may not appear as very pragmatic from the host environment’s perspective, sometimes, it doesn’t even make sense, or in some cases, it’s pretty difficult to replicate a similar solution in the original language. Often there’s a perception of “being a step ahead”. David Nolen frequently mentions that. You don’t need to be in the mainstream pool to create cutting-edge solutions.

        Writing code in Lisp dialects doesn’t really feel like you’re working in a niche. It feels like achieving generality through specialization. I don’t feel like I don’t know Lua, even though I chose Fennel to work with Lua. Certainly, I am not a Javascript illiterate, even though it’s been years since I had to write and support serious Javascript code; I deal with it through Clojurescript. I have never shipped any serious Java projects, but I can switch to it easily - Clojure taught me some Java. If I ever need to deal with low-level C code, I’d probably pick Janet. And you can pick any platform - most likely, there’s a Lisp dialect for it. I don’t have to “justify my Lisp/Clojure fetish” - these are the set of instruments that help me get my job done. I’ve never heard tales of Clojure/Common Lisp developers failing to program in a non-lisp language after years of using Lisp. Not wanting to program in a non-lispy language, obviously is a different matter.

      2. 16

        If you master some really niche tech where companies find they absolutely need an expert in it every now and then, the companies come to find you.

        Via a failed startup many years ago, I ended up learning macOS device driver/kernel programming. My activity on GitHub and Stack Overflow related to this subsequently led to one contracting/consulting gig after the other. These days most of the work centres around macOS’s transition to DriverKit, which is so complex and poorly documented, there is a fairly high bar to getting started with it without the context of the tech it replaces. So most of the time, internal developers try it, run into problem after problem, most of which end up leading them to a Stack Overflow question I’ve answered. Eventually many conclude it’ll be easier if they just get me do the project altogether.

        1. 12

          These days most of the work centres around macOS’s transition to DriverKit

          This comment confused me a lot but it turns out that Apple is recycling names again. I thought you were referring to DriverKit, the Objective-C device driver framework that shipped with NeXTSTEP, which Apple abandoned with Mac OS X 10.0 and replaced with IOKit, a C++ kernel driver framework. In fact you were referring to DriverKit, the userspace C++ driver framework that Apple introduced in macOS 10.15.

          This is almost as confusing as when they decided that the version of Objective-C that came after Objective-C 4 was Objective-C 2.0.

          1. 1

            have not written anything with the Driver and IOKits but from reading the docs, the old DriverKit feels so much nicer than IOKit. Wish the BSDs had something like that instead of having to simulate Objective-C in C. For a while, I hoped you (@david_chisnall) would put Pragmatic Smalltalk into the FreeBSD kernel. What a wonderful programming environment that would have been! NetBSD does have a lua binding but Smalltalk reads so much nicer. Pragmatic Smalltalk is/was almost there… using the objc runtime means we get to write bindings in Objective-C – something that the language was originally designed for.

            1. 2

              For a while, I hoped you (@david_chisnall) would put Pragmatic Smalltalk into the FreeBSD kernel.

              I actually had an intern do exactly that. He also worked on a modified runtime designed for very small numbers of methods per class. It worked but I didn’t see a path to community adoption. In particular, Objective-C has become increasingly dependent on exceptions, which we definitely don’t want in the kernel and I had implemented Smalltalk non-local returns using exceptions except in cases where the block could be inlined (basically, conditionals and loops on things that the Objective-C type info tells you evaluates to a BOOL).

              In the kernel, I’m not sure I want duck typing, I want as much to be statically checked as possible. I have ported libc++ to build in the FreeBSD kernel and C++17 is quite a nice programming environment for kernel use (writing kernel modules in C++ is noticeably less verbose than in C) but the kernel’s loader needs some work for it to be useful (I wanted to be able to have a libc++ module and have other modules depend on it, but there’s no handling of COMDATs in the kernel loader). The lack of exceptions is also a problem for C++, since a lot of the standard library classes handle failures by calling abort (panic, in the kernel) on errors if compiled without exceptions. Maybe it’s worth trying to port SerentyOS’s libraries instead.

              1. 1

                I actually had an intern do exactly that. He also worked on a modified runtime designed for very small numbers of methods per class.

                Interesting. Any chance it is open/available? Would be interested in trying it out.

                I didn’t see a path to community adoption.

                NetBSD perhaps? much better base for experiments and I for one would like to try and work on this on NetBSD.

                In particular, Objective-C has become increasingly dependent on exceptions, which we definitely don’t want in the kernel and I had implemented Smalltalk non-local returns using exceptions except in cases where the block could be inlined (basically, conditionals and loops on things that the Objective-C type info tells you evaluates to a BOOL).

                couldn’t exceptions be dropped? since the runtime environment is severely restricted anyway, a subset that is suitable for it could be defined and used no?

                In the kernel, I’m not sure I want duck typing, I want as much to be statically checked as possible.

                agreed, but the nice thing with objc is that the types are not lost at runtime, which can be quite useful. A Smalltalk-like scripting interface, say through a workspace/transcript device nodes, would be a nice way to inspect and maybe even make small modifications to the running system.

                I have ported libc++ to build in the FreeBSD kernel and C++17 is quite a nice programming environment for kernel use (writing kernel modules in C++ is noticeably less verbose than in C) but the kernel’s loader needs some work for it to be useful (I wanted to be able to have a libc++ module and have other modules depend on it, but there’s no handling of COMDATs in the kernel loader).

                Bareflank[1] seems to use a custom loader for their C++ runtime, also based on libc++.

                [1] https://github.com/Bareflank/hypervisor

                1. 1

                  I actually had an intern do exactly that. He also worked on a modified runtime designed for very small numbers of methods per class.

                  Interesting. Any chance it is open/available? Would be interested in trying it out.

                  I don’t think so. It was 10 years ago and, even if the code were around, it would be quite bit rotted by this point.

                  I didn’t see a path to community adoption.

                  NetBSD perhaps? much better base for experiments and I for one would like to try and work on this on NetBSD.

                  Possibly. NetBSD put Lua in the kernel before everyone else but I’m not aware of anything significant using it.

                  In particular, Objective-C has become increasingly dependent on exceptions, which we definitely don’t want in the kernel and I had implemented Smalltalk non-local returns using exceptions except in cases where the block could be inlined (basically, conditionals and loops on things that the Objective-C type info tells you evaluates to a BOOL).

                  couldn’t exceptions be dropped? since the runtime environment is severely restricted anyway, a subset that is suitable for it could be defined and used no?

                  Yes, you can build without exceptions, but then you’d need some other error-handling mechanism. In more modern languages, this is typically some form of option type.

                  In the kernel, I’m not sure I want duck typing, I want as much to be statically checked as possible.

                  agreed, but the nice thing with objc is that the types are not lost at runtime, which can be quite useful. A Smalltalk-like scripting interface, say through a workspace/transcript device nodes, would be a nice way to inspect and maybe even make small modifications to the running system.

                  True, although I’ve recently been using Sol3 with C++ and, although it needs a little bit more glue, it’s fairly small.

                  I have ported libc++ to build in the FreeBSD kernel and C++17 is quite a nice programming environment for kernel use (writing kernel modules in C++ is noticeably less verbose than in C) but the kernel’s loader needs some work for it to be useful (I wanted to be able to have a libc++ module and have other modules depend on it, but there’s no handling of COMDATs in the kernel loader).

                  Bareflank[1] seems to use a custom loader for their C++ runtime, also based on libc++.

                  Do they support kernel modules? There weren’t any problems compiling C++ into the kernel, or even within a single kernel module, the problem was using C++ types across a module boundary.

                  1. 1

                    Do they support kernel modules?

                    I posted the wrong link. [0] seems to support kernel modules, haven’t checked it out yet though, only watched a couple of talks [1, 2] :)

                    There weren’t any problems compiling C++ into the kernel, or even within a single kernel module, the problem was using C++ types across a module boundary.

                    Missed the “types across module boundary” part. I was thinking only about a single kernel module and pointers to working examples of C++ kernel modules appreciated.

                    [0] https://github.com/Bareflank/standalone_cxx

                    [1] https://www.youtube.com/watch?v=uQSQy-7lveQ

                    [2] https://www.youtube.com/watch?v=bKPN-CGhEC0

                    1. 1

                      Missed the “types across module boundary” part. I was thinking only about a single kernel module and pointers to working examples of C++ kernel modules appreciated.

                      I didn’t dig deeply into the problem, but there are some subtle problems. FreeBSD kernel modules are really .o files, not .so files and the module loader does the equivalent of static linking. This makes sense because a kernel module is loaded zero or one times in a given system and so you don’t need to pay any of the costs of a shared-library code model (PLT indirections and so on), you can apply relocations into code when the module is loaded and before you mark the pages are executable in the kernel’s address space.

                      Handling things like C++ inline declarations (and template instantiations) is tricky here. Normally, the compiler emits these in every compilation unit that references them but then the static linker discards the unused ones. I think the right solution to this is to have a ‘link’ phase that examines the modules that you depend on and discards any COMDATs that are present in those but even that can lead to problems when you have modules A and B that both depend on C and include a template instantiation that C doesn’t, because now you have two definitions of the same symbol in the kernel. Solving that is nontrivial.

                      It looks as if standalone_cxx doesn’t have a solution to this, they assume a full link, so don’t handle any form of dynamic loading.

              2. 1

                I sort of wish for an Objective-Zig or something – the same marriage of Smalltalk with a (better) version of C for the fiddly memory bits. I miss writing Objective-C, partly because of the discoverability of the Objective part.

          2. 9

            I like this very much and sympathize. I worked (and managed) a team of C developers. While I definitely do not consider C a dying language, I think it is definitely a lot less mainstream than Go, C++, Java, Python, Ruby, JS and its derivatives. We had real trouble finding developers, especially that our salaries were not as competitive as we advertised, and the really good ones we found basically dicatated their minimum ranges before starting the interview.

            1. 7

              I think there’s definitely a bunch of differences in various areas of the world here. The one time I did a interview for a London-based Clojure role it involved a terrible tech test with me writing Clojure by hand on paper.

              1. 6

                writing Clojure by hand on paper

                I’m trying to imagine how this would work and it’s just not coming together in my head. Do you use like … different colored magic markers for syntax highlighting? Asking people to write code without paredit feels almost inhumane, much less not having access to a repl.

                1. 19

                  I’m picturing a red button on the desk, and every time you write an unbalanced “)” the interviewer hits it and there’s a BZZZZZ

                  1. 6

                    It was just me and a biro. It was excruciating and I kept asking the interviewer if I could take my laptop out of my bag and do it there, and he kept refusing. These days I’d just walk if they suggested I do such a thing.

                    1. 3

                      These days I’d just walk if they suggested I do such a thing.

                      Bingo.

                      There’s a point at which even if you pass the interview, if the process was so broken, you know that if you take the job you’ll be stuck only working with co-workers who also passed the broken process.

                      1. 1

                        Jeez. I’d have just asked the interviewer how often they wrote their clojure code on paper.

                      2. 4

                        you could simply drop the parens altogether. You can get away saying something like “you are not supposed to see/count parens anyway!”. /me ducks.

                    2. 5

                      In the 2030s, C programmers will get to have their COBOL-programmer-in-1999 moment. Relish the market opportunity.

                      1. 5

                        There’s another really cool benefit from adopting a niche language (in my case, Elixir): There’s a selection effect at play with these languages. Your colleagues tend to be people that care deeply about the craft (otherwise they’d work on something with a stronger job market like C# or Java), and that means you reap the benefits from the passionate people you get to work alongside in addition to the niceties of the language itself!

                        1. 3

                          I regret not leaning into this direction after my first stint at Apple, when I was pretty much the only person on planet Earth who fully understood the iTunes (and Amazon, funnily enough) video ingest process.

                          1. 3

                            Question: is becoming a niche programmer possible for someone with little to no experience? Is it a good nontraditional route to enter the industry?

                            1. 5

                              I think it depends on the niche? For Clojure, my sense is that you can pick it up with little or no experience, but you have to be intelligent in a particular way

                              e.g. In my experience probably some smart music majors can pick it up, but others might have problems. That is, people “just looking for jobs” will likely have issues with Clojure. There is a tendency to do more from first principles and not follow canned patterns. Also smaller number of StackOverflow answers

                              1. 5

                                Absolutely, my previous job hired lots of interns to work with Clojure who had very little programming experience. The interesting part we found was that people without experience often have an easier time learning Clojure because they don’t have any preconceptions about how code should be written. A few of the students my team hired ended up specializing in Clojure after and haven’t had to work with anything else since.

                                Since Clojure is niche, companies end up having to train most of their devs, so having familiarity with the language is seen as a big plus. Having some small project on GitHub that you can link to in your resume would go a long way here.

                                1. 2

                                  I think all this post shows is that it’s possible to do much better than the mainstream in a niche, but it’s also possible to do a lot worse.

                                  The thing about a niche is it’s unique, so how can you generalize about it? It completely depends on what niche it is.