Interview with Douglas Crockford
evrone.com
evrone.com
> Evrone: You spread the idea that developers should read each other's code regularly…
> Douglas: In filmmaking, there is a time in the morning called "dailies", when the previous day's footage is examined. It looks like everyone is just sitting around watching movies and wasting time, but it is critically important in finding problems early and assuring the quality of the product. I believe that we should do the same thing in programming. We have a time every morning when the team gets together and reviews all of the code and designs that were developed the previous day.
That doesn't mean I think Crockford's "Dailies" model is a poor one, it's just one issue to be aware of. Though I often find the same in Scrum teams. Folks aren't listening, they're just waiting for their turn to speak.
We need to accommodate human nature, not change human nature to accommodate corporate requirements. Its irrational to expect a group to rally passionately around every available task. Robotic.
The other part is that you can also grow people into being good. This is one of the more important roles of big-tech where college grads come in as n00bs and become engineers.
I think what Crockford outlines here would be fantastic if it worked. Multiparty code review.
The film shoot is a special high-intensity time. You may spend months and months in pre-production, but the actual shoot might only be 20-30 days (on a lower-end production). Every day needs to count. If you can learn from the previous day and improve, it makes a real difference near the end of the shoot when you’ll have much more freedom to experiment.
In contrast, software engineering is more of a uniform slog. Back when software shipped in physical boxes, there used to be a marked difference between the planning, development and testing phases, but that’s gone. At many companies it’s just a grind of tiny features and endless tickets. Hard to get excited about the “dailies” for that.
Dailies in film is watching not quite the final product, but still an output of the process. Maybe the issue was reading code as a team (that's code review/PR) rather than reviewing the output of the code? I think Dailies as a metaphor to me sounds like a call for more regular QA/UAT style review sessions as team. (This metaphor is new to me, but regular "use the product as a team" sessions might be quite useful, just maybe not every day.)
One hour, every morning, where the entire Team reads the code written the previous day and offers feedback. Juniors learn from seeing real-world code presented and explained, Seniors get peer review, more eyes means fewer bugs, etc.
It didn't work for my Team, but I would be interested in trying it again in future.
That statement works in so many contexts
I will do something similar as part of big product releases though: I'll get the team together and we'll do a once over walk through over everything end to end. Normally this produces about an additional sprint (two weeks) of work ahead of the launch as you uncover problems and people see things with fresh eyes. So make sure to build that time into your schedule.
(As an aside, I think I understand a bit more about Lisp macros after contemplating this... Properly applied macros and DRYness allow you to elimiate much of your "simple" boilerplate, so you're left with beefier chunks of functionality. Like everything else, though, YMMV.)
I started to get a bad feeling when the class keyword became popular, but all of the old good parts are still there.
I never really liked any of the JS's attempts at OOP, be it the original prototype-based or the newer class-based, but I think that people were sufficiently confused enough by the prototype system that most people didn't bother with it. The class stuff is more approachable, and for some people that can sort of be viewed as a bad thing.
> true OOP languages
Specifically the Java used as a example isn't exactly a "true OOP language" either; in a sense it's actually a very leaky imperative VM with implementation details like non-object Primitive Data Types and very visible and prominent imperative flow control structures.
Javascript is much closer to an actual "true OOP language" as, unless you're dealing with bad/naive bindings to other stuff in C, it's very hard to even get a handle of a non-object.
1. Particularly in pure JS projects which either haven’t yet or won’t migrate to TypeScript, classes are an excellent way to define data types. They needn’t be stateful, they can be used just like POJOs in otherwise pure functional code. But their shape is clear (or can/should be), and can be used as both types and runtime references without a lot of ceremony.
2. Classes, when they have a clear shape, are great for performance-sensitive code. POJOs can achieve this, but it’s frequently all too easy for their shape to evolve as a codebase is in maintenance. And that can cause hard to find performance regressions.
That `class` syntax works in all the modern browsers today, no Typescript transpile or Babel plugin or "polyfill" needed.
(Dojo comes to mind because of doing code on current ArcGIS-related projects that are still stuck in the mines of ancient Dojo versions, IIFE "classes", and AMD wrappers. Still great to have Typescript generate the AMD wrappers for you from modern ES2015 module syntax, but `class` and even `await/async` you can get for "free" in modern browsers and are huge clarifying changes to legacy Dojo work.)
- Constructing flat POJO values is trivial (spread + define any changed properties).
- Immutable array operations are fairly robust, albeit not as expressive as some may want. Getting comfortable with reduce improves this dramatically.
- Map/Set constructors accept instances to create new values.
- Class instances used as value types are simple to derive, assuming their constructors accept objects in their shape. If they have a consistent shape, deeply nested clones with consistent descendant classes are equally trivial.
- Iterators could play nicer with other native interfaces, but they’re pretty handy even so. Custom iterators are exceedingly handy for shuttling data between types.
- More expressive operations over sequence-like structures is as simple as operating on iterators. This will feel more reasonable if you favor Map over POJO for mapped types.
- Imperative stateful functions are admittedly a pain, but wrapping them in functions which return meaningful state you’ll need later is pretty straightforward.
- Really though, starting from a functional perspective just makes these details a non-issue. If your primary tool is a pure function, any gap in these interfaces can be filled by writing a function and calling it rather than the imperative code.
If some or all of that sounds like it’s a performance nightmare… most of the time it isn’t. But if you have a hot loop where copy on write is a meaningful bottleneck:
- You can take inspiration from Clojure or other functional languages with persistent data structures. There are a ton of libraries.
- You can take inspiration from Clojure or other functional languages with a concept like transients, and isolate your mutable code in a function body. This doesn’t even need a library, it’s just bailing to the imperative code the language ostensibly wants you to write in the first place, and being thoughtful about how it’s encapsulated.
What I do find painful and tedious is trying to follow stateful imperative code which hasn’t been subjected to intense discipline. This is true regardless of the language. It’s true whether the code is “object oriented” or not.
Your list sums up why I find it tedious ;), since you mention Clojure, in Clojure one doesn't have to think about those things and go through hoops(except for when doing Java interop). Unfortunately I don't think Clojurescript is worth the hassle(or even immutablejs or immer) and just prefer writing JS and applying your list.
Functional programming languages spoil you in this regard.
Weird!
> Unfortunately I don't think Clojurescript is worth the hassle(or even immutablejs or immer) and just prefer writing JS and applying your list.
Seems like we agree actually?
Famously Ramdajs on its front page has: we're not porting over all of the Clojure functions. To slow down this direction, for good reasons perhaps. Still its very visible that FP idioms travel nicely between langs/platforms.
I actually like many of the new parts - the class keyword being a notable exception. The other (much much milder) exception is async/await syntax - I love Promises but the async/await sugar on top of it this seems a little too much like magic. At the end of the day, they're still just Promises, just weirdly abstracted (just as classes are still just prototypes, just obscured so they look like something they're not).
The odd thing imo about the class keyword is it doesn't even seem to "fit" with the direction of may of the other new changes. E.g. arrow-functions are a very un-classy simplified functional form (no this re-assignment). This is very evident in the direction of many frameworks - e.g. React quickly moved to using classes (extends React.Component) and then quickly moved away from that convention (function components) as if realising their mistake.
Promises can get a little unwieldy raw - working on making that easier is a noble goal. But I'm never a fan of systems that actively obscure how something works. We now have plenty of people throwing asyncs and awaits around their code without a clue about how Promises work. It's significantly easier to learn how specific control flows work while writing raw Promises.
But they are not common links, which is stupid. Hovering the "link" doesn't display the URL in the browser, right-clicking doesn't understand it's a link (because it's not), you can't open it in a new window as the creator of these "links" didn't consider that behavior (shift+click) and they are not accessible in the least.
TLDR: author re-implemented their own links, with more drawbacks than they could imagine
Obviously this can be a blessing or a curse depending on who's writing the code; it's not terribly hard to write yourself a spaghetti mess of chaos in JS. Callbacks lead to a mess of nested lambdas, promises are better but still a bit clunky, and trying to work within the "few core types" led to the famous "wat" video [1], so these things come in tradeoffs.
Still, I see where Crockford is coming from (at least if I understand his point correctly). My interview language of choice is JavaScript, and I rarely use anything introduced from 2015 or later (except the shorthand function syntax). I like that the core language has basically no bureaucracy, and you can focus on just writing code.
Believe it or not, you can actually structure things so it's "flat" looking without async/await (or even without Promises for that matter, but Promises solve other problems too).
Just because someone argues against something like async/await, doesn't mean they want the thing async/await is supposed to address.
One of the things Promises solves is nesting. If you're nesting them, you're doing it wrong.
(I don't mean "never nest them"... but if you find you're nesting them, see if you can refactor to "all()" them or similar)
The hard part, of course, is threading complex flows of variables through your .then() callbacks where later Promise calls need a previous variable or three, and that's when a lot of people give up and just resort to deeper nesting. That's what async/await does the best at solving: capturing variables automatically in a simple state machine written like classic imperative code versus manually trying to thread state through callback closures and complex return types.
I'm also "against" the constant syntactic sugar that gets added with little to no benefits, just leading to JavaScript having a large syntax.
Why introduce "classes" which are just sugar on top of existing syntax? Many examples just like this, where sometimes it feels like JavaScript has changes just to have changes.
Although I'm not gonna complain too much, many of the new features are more than just syntactic sugar and makes my life a lot easier. I just wish they were slightly more conservative I guess.
Also, avoiding the for loop in JS might be a good idea because of how easy it is to shoot yourself in the foot with "for (x in stuff) ..." creating a global variable (when you accidentally leave out "var").
[1] Probably this one? https://www.youtube.com/watch?v=XFTOG895C7c
You said that, despite the bloat, you won't complain too much because some features make your life easier. That's precisely what I was saying!
Note that, no where did I argue that bloat is good. I only said that it was tolerable given that some new features are useful. Maintenance burden is a real problem, but is still tolerated in practice.
I don't agree that subsetting a language is far from an academic approach. In fact this is what pretty much every PLT class does in order to make reasoning about language definitions easier.
i do agree that there’s a need to weigh the value of new features vs bloat but id say 1 good feature in ten is pretty poor odds.
another example of crockfords pragmatism is JSON which for all its flaws has been a real boon.
edit: un-auto correcting “crockpot” :D
That being said, I really wish JSON had comments :P
I think that's a pretty interesting statement from somebody like Douglas Crockford. Not disagreeing; but still ...
I think there are two ways of looking at recent changes in Javascript. Either it's progress or it's just not enough progress. I think what he is saying here is that there it has too much baggage and that there are newer and more interesting languages now. That's not such a strange thing to say for somebody that has been involved with trying to create new languages.
My personal view is that browser Javascript is mostly a compilation target these days, including for Javascript itself, and that we now have a better compilation target in the form of WASM that is less awkward to target for a lot of languages as well. The question is what languages people will be using in the next years that target that. I'd say there are some interesting alternatives to choose from already and there are likely to be more in the next few years.
I was wrong, though, tail calls are in ES14, and eliminating them is probably not implemented anywhere yet.
Edit: Wrong again, tail-call optimizations are in WebKit; iOS has had them since iOS 12 according to this: https://kangax.github.io/compat-table/es6/#test-proper_tail_...
Fairly sure it wasn't really an "interview", and just an emailed response to a list of questions.
Strange that he says this, because the original "Good Parts" principle is that you can use the "good" subset of the language.
No matter how many new features they add, that same "good" subset is still there ready to use due to backward compatibility.
Linux is probably the last time something was really built from scratch. Most commercial software in use today is dependent on code written in the 80’s and 90’s.
So, yes, there is a need for new software to match the new paradigms, but the task of starting from scratch is colossal.
Linux was started in 1991. Windows NT (whence Windows XP and modern Windows versions derive from) dates from at least 1993 (I don't know when the code internally started, but the first release was 1993).
For browsers, Firefox source code originates from Netscape 6, which was a rewrite from scratch and shared little to no code with Netscape 4 or earlier. This rewrite happened in 1998. IE dates from 1994, per Wikipedia. KHTML, whence Safari and Chrome ultimately derive, is itself from 2000.
Let's talk about programming languages. Java dates its early work to 1991. .NET Framework is the late 90s (Wikipedia is exceptionally poor with dates here). The LLVM framework dates to 2000 in the earliest. Accelerator programming languages like CUDA and OpenCL are deep into the 2000s in their genesis, and the development of more fully heterogeneous programming languages obviously postdates that.
That's a lot of significant projects that date way later than Linux.
And yes as well on anything serious that deals with modern CPU architectures.
But my point was that the old stuff has not been replaced, it’s still out there and often times you don’t have to dig much to find it. Particularly at the OS level, in memory management, multithreading, networking stacks and hardware drivers.
So the jungle simply becomes more diverse and nothing ever gets retired.
Lord, I came across a jumble of Perl code just a few weeks ago.
I'm curious about what caused the sudden down peak?