Well, this has a lot of practical knowledge on offer, and I intend to watch it a few more times for it all to sink in.
I've always believed that anyone can learn anything, if they and their teacher both believe it, and put in the effort. I'm glad to see that bias confirmed.
I was really surprised that nobody seems to actually handle errors. This does reflect my experience, in what I thought (until now) was an exception. In about 1985 I was taking a vendor taught course in PL/N at the Norand company (who made hand-held computers used for inventory management). We got to the section of the course about error handling. They described the syntax for checking errors, and how to format things, etc. I asked what happened when you got an error, and both instructors had never been asked that question, and both had ZERO clue about it.
For all the arguments here about in and out of band error handling, this was shocking, and definitely did not confirm any bias.
I'm a big fan of "Railway Oriented Programming" [1]. Basically your code has two paths - the "Ok" route and the "Error" route. Your code won't compile unless you handle the Error case.
However, it requires language level support to use easily - you need mutually exclusive types like discriminated unions. It's harder to do in most OO languages like C#/Java/Javascript, but you can do it with some effort. Which is why most don't.
While ROP is better than some current ways, I vastly prefer dataflow (pipes/filters) as a technique.
The fundamental problem, IMHO, with error-handling is the call/return nature of most of our programming languages. That means you have to return something from you function/procedure. If you have nothing to return (error) or nothing to return yet (async), you have a problem, particularly with intermediate functions that don't really know what happened below them and don't really have enough of the context from their callers to handle the error.
Exceptions allow you to skip the these intermediate layers. ROP makes you handle all those layers, but gives you some tools to help make sure you did it correctly.
Filters, on the other hand, do not return results. Instead they actively pass their results on to the next filter in line. That means that in the case of an error, they can do what functions/procedures cannot: simply not produce a result at all. The next filter will be none-the-wiser.
You can then have the filter have an out-of-band mechanism for actually dealing with the error, analogous to Unix stderr.
> The fundamental problem, IMHO, with error-handling is the call/return nature of most of our programming languages
I actually consider this a benefit, not a problem. The alternative, as I see it, is to side effect... which 99% of the time is a bad idea if there's an alternative.
> particularly with intermediate functions that don't really know what happened below them and don't really have enough of the context from their callers to handle the error
If you write a `map` function, you can use it in your pipeline to map over the Ok case and your intermediate functions don't need to know they're taking an Ok|Error type. This is exactly the same as what occurs in the `map`/`select` higher ordered function that's on the `list` type. The function passed to map/select doesn't know that it's being used on a list. In the same way, the function being passed to `Ok.map` doesn't know that it's being used on a discriminated union.
> You can then have the filter have an out-of-band mechanism for actually dealing with the error, analogous to Unix stderr.
To me this is a side effect and should be avoided. Basically your filter is partitioning your list into two lists, one of OKs and one of Errors, logging the errors, and then not reporting anything back to the consumer. LMK if I'm misunderstanding.
Well, I think you are filtering things through a lens that is so limiting that it might as well be misunderstanding. This lens, this filter, is so ubiquitous that as far as I can tell, most people can't recognise it. I call it The Gentle Tyranny of Call/Return [1][2][3]
> side effect... which 99% of the time is a bad idea
Nope. A filter does not have "side" effects. It has effects, as in what you want to achieve. And it turns out that having an effect is a good idea about 100% of the time, because otherwise your program is 100% useless. Now when your primary architectural style is call/return based, effects of the program that are not encoded in the return value do become "side" effects, but that's a limitation of call/return (which FP turns up to 11), not a problem of effects in general.
> > You can then have the filter have an out-of-band mechanism
> To me this is a side effect and should be avoided.
Why? Apart from the religious mantra of "it is a side effect"?
When I adopted this pattern, it turned out to be hugely beneficial. It keeps your happy-path happy, no need to pollute it at all with error handling concerns. And it lets you customise and centralise your error handling, which turns out to be what you usually want, but without the call-stack shenanigans of exceptions.
ROP kinda sorta attempts to do something similar, but due to the constraints of C/R, it turns out to be far more complex.
Scala has some nice containers around this with Either, Try, and Future. We’ve also made some custom hybrids like FutureEither. And then with basic for-comprehensions/maps you can chain together logic and if something fails it’ll return an either with a Left containing an error response, otherwise it’ll implicitly keep going with Right artifacts.
There's a huge bias towards the happy case in specifications, user stories and so on.
I try hard to discuss error cases, but often I only get a very unenthusiastic "well, show an error message, d'oh" as a response, which wears me out over time.
This is worse for non-interactive things, like event handling. There's no person to show the error to, usually logging and aborting are kinda the only possible actions, which is endlessly frustrating.
In the systems I build, I always have the lambda's throw the error to Slack. I have been told many times, that is crazy, and that it doesn't scale. By guess what, when a developer's errors are blowing up a slack channel, they always get fixed.
There is a risk with it that you just ignore it. We always put it into a separate room for dev staging and prod. So, what will often happen is you might start to see more errors occurring once you push to staging and your tests are running. It doesn't replace tests and the like, and isn't a cure all in terms of how to handle errors, but I have found it pretty helpful. Especially with serverless, because observability is one of the hardest parts of this paradigm.
> I asked what happened when you got an error, and both instructors had never been asked that question, and both had ZERO clue about it.
No comprendo. Did you mean, what was supposed to happen?
Sounds very context-dependent to me. Sometimes you'll pause and show the user the error, sometimes you'll log it and move on, and if it is fatal you'll shutdown immediately to prevent further harm.
The language had error handling constructs, but in the years they'd been teaching the course, nobody had ever asked how the error handling actually was to be used in practice. I found it shocking that nobody cared about error handling, especially on a platform that generated programs to be used by non-computer literate civilians.
Perhaps the instructors had no practical industry experience, it happens.
In my experience, newbie students are working hard to get things working correctly in simple cases. So error handling feels like a luxury at that point.
I also believe anyone can learn anything, exactly as you said!
But I don't find the study about grade distributions a convincing argument for that, unfortunately. Grades are a measure of student's ability to apply consistent effort on assignments and effectively study for an exam, often collaborating with a network of other students to find the correct solutions.
A determined student can earn a good grade. A determined student can master the material. There is overlap, but not enough to use a premise that Grades<=>Ability
A more convincing study would find a way of evaluating their individual ability to solve problems related to what they are supposed to have learned, not their grade in the course.
> I also believe anyone can learn anything, exactly as you said!
> But I don't find the study about grade distributions a convincing argument for that, unfortunately.
That does indeed seem like a faulty argument. Wouldn’t there be heavy selection bias? It seems incorrect to draw the conclusion that anyone can learn programming based on the grade distribution of a university level CS class.
> Perl is as hard for novices to learn as a language with a randomly-designed syntax
Make something accessible to novices IMHO is a wrong goal in many cases. A tool like a programming language used day to day for many months or years. What important is how efficiently you can use it after spending enough time to learn it (I would say a month or two should be enough for most languages if you know already a few, but it depends).
Of course we cannot ignore how hard is to learn a language otherwise we will have languages like C++ which require years to master it (and even coders with years of experience fall pray to numerous hidden traps of the language).
I think that's a false dichotomy in many cases. For example, compiler errors. Elm and Rust have really good errors, and that benefits everyone. Once that part is done, I agree with you.
I also think you may overestimate the depth of knowledge that the average programmer has about his most used programming language. Languages evolve a lot, sometimes faster than how you used them. People also don't use the same programming language during all their life.
An interesting research question would be if the things that make it harder for a novice to understand code are exactly the same things that make experts more productive. Curly braces was used as an example in the talk, and for me at least they are very useful to understand code structure. I still remember that one of the hardest things for me to get started with in Java was the seemingly endless types. There was a general overload with phrases I had never seen before coming from Python. While I am not too strongly opinionated on static vs. dynamic typing today, static typing is very popular among expert programmers.
The best explanation for Java language design I have heard that it's not designed to be productive but to prevent mediocre developers from shooting themselves in the foot.
Did you mean AbstractCorporationCategoryLanguageMemeFactory?
Anyway, at the time Enterprise Java was developed, it was commonly believed that the best way to do large scale development is to hire busload of outsourced workers and give them tools that don't let them do too much damage. If you let them have C++, you'd end up with Series 60.
I agree. This is something I tried to explain to others in my last job, with little success.
A person using a language long-term is only a novice for so long. After they've figured out the basics and become comfortable solving problems, they become interested in efficiency.
Designing a language to cater to novices is like designing a bicycle with only low gears—it's easy to get started, but increasingly difficult to move fast. I.e. the Turing tarpit [1].
I'm biased, and admittedly don't have a lot of perspective on this topic, but I have seen the effects of poorly-designed languages on the application development process and its output.
After the "toy" languages of Basic and Pascal, my first real programming language was PDP11 Macro-11 Assembly. At the end of that programming class, the final assignment combined all the prior assignments and to the entire class's surprise we had a C compiler. From that laying a foundation experience, I felt extremely comfortable when learning and working in C for a good 20 years after graduation. C++ appeared first as a pre-processor for C, and then took over as the primary compiled language with C treated as a less favored step child. Over the years, my experience has been those that tried to embrace the entire language shot themselves in the feet over and over again: template programming has always been unstable and difficult, operator overloading is a recipe for mistakes, and complex object hierarchies over designed to a fault. Nobody appears to follow the Keep It Simple Stupid (KISS) principal anymore. If there's complexity, it should be innate to the product, and not reflected by how to product is constructed internally. The portion of the talk where it is discussed how many parameters and features of software are never used hit my experience the sharpest. Keep Is Simple!!!
Too many guards and training wheel preventions? Lack of adherence to a loose standard causing incompatible language variations from different vendors? Pascal editors often enforced a code layout style, with different editors enforcing different styles? No serious work done in pascal?
In slide 18 Fucci2016 is referred for a replication study with 39 professionals. That study only uses students. That slide quotes Fucci2016, but I do not see that quote in the paper. He talks about TDD and hatemail and is not careful about references? Perhaps this was just a small error - the reference is incorrect. I am not sure.
It seems believable that Test-first vs Test-last might have similar impacts. I just checked one thing in this talk and it turned out to problematic. Has anyone else done fact-checking on his stories?
It seems he took the title of a different Fucci 2016 paper, it should say "A Dissection of the Test-Driven Development Process: Does It Really Matter to Test-First or to
Test-Last?"
This paper contains both the quote and is done on 39 professionals. This seems like an easy mistake to make and not something I feel has an impact on the trustworthiness of his talk
Thanks! Seems like short cycles of development including interleaving testing - is the key in this paper. TDD might just achieve this by a fluke, but it should help that - unless you do this TLD or even test addition in cycles. Any thoughts on that?
I am confused by this comment and the overall sentiment in the video.
Short dev cycles isn't an accidental byproduct - it's literally one of the key parts of tdd.
The first google result for tdd has this:
> Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards.
What he said about developer productivity might be technically wrong, but misleading. Certain types of work require one to be at certain level of mastery, not just in software by the way. One who performs at that level can do the job, but just barely. Below that level you are a burden, not an asset, whoever worked in the industry knows such people, they don't do anything productive, but mostly get in the way. If we then assume that this level is zero, then everything above it it is positive, and or measurement of how much one engineer is better than another is exaggerated.
It's like when you are playing an RPG. Quest that requires level 5 is pretty much impossible for level 4. Doable for level 5. Level 6 can pass one easy. Level 10 would zoom through it. He's not 10 times higher in level than level 5, but certainly would get the job done 10 times faster and more reliably. That's where this comes from. Small advantages can mean difference between failing the project, barely pulling the project, or acing one; all of which translates into business losing or making money.
> Lots of code metrics have been proposed ... But nothing works better than counting lines of code [with citations]
Here's an anecdoate, not to dispute the empirical finding.
I recently discovered McCabe (cyclomatic) complexity and found it remarkably good! In a tiny experiment, I ran it on a 50k Python code base with a threshold of 8, and the output almost exactly matched all of the already existing `FIXME: refactor this` comments. According to McCabe the threshold to look out for is 10.
---
p.s: if you write Python, you already probably have the mccabe package installed through flake8. Use it like so:
It truly changed the way I think about not just software development, but it changed my entire view of the world, which is more than I can say of any other presentation. Granted, I was still at a fairly impressionable age of 17 when I listened to it, but still.
Definitely looking forward to listening to this one too.
Since most people don't handle errors, I think including some testing functional knowledge of error handling might turn out to be surprisingly effective at gauging how effective a programmer truly is.
I've always believed that anyone can learn anything, if they and their teacher both believe it, and put in the effort. I'm glad to see that bias confirmed.
I was really surprised that nobody seems to actually handle errors. This does reflect my experience, in what I thought (until now) was an exception. In about 1985 I was taking a vendor taught course in PL/N at the Norand company (who made hand-held computers used for inventory management). We got to the section of the course about error handling. They described the syntax for checking errors, and how to format things, etc. I asked what happened when you got an error, and both instructors had never been asked that question, and both had ZERO clue about it.
For all the arguments here about in and out of band error handling, this was shocking, and definitely did not confirm any bias.