I received a nice email last week from a professor at a major university, who asked:
I'm wondering if you can share with me some advice on how to train inexperienced graduate students to be productive in writing quality code in a short period of time.
First: I'm flattered that this person thought I have any advice worth giving! My second thought was: It's impossible! But after thinking it over, I came up with one suggestion. In hopes that someone else finds my reply useful, here's what I wrote in return (with a bit more editing):
I think the best thing for improving code quality is to require your students to write a corresponding set of unittests along with any code they write. Python has a wonderful unittesting module that's built-in, as well as a good mocking library. I would encourage your students to also run a test coverage tool periodically to understand what parts of their code isn't tested.
Unittests are like an insurance policy for change. They're especially important for languages like Python that have no other way to verify correctness at even a superficial level. When your code has unittests, it becomes a lot easier to modify and expand a program over time while preserving confidence in the functionality you've already built.
Even when I write code in my free time for fun or personal projects — here's a recent example — I still write tests because they help me build functionality faster overall. The time it takes to write tests is far less than the time it takes to fix all of the bugs introduced unknowingly when you don't have tests. It's worth acknowledging that testing may feel like a waste of effort sometimes, but it's important for your students to understand that taking the time to write tests will be more efficient overall for any program that's non-trivial.
My last piece of advice is this: Sometimes people say that something was too hard to test, so they just skipped writing a test for it. This is exactly the wrong conclusion. If your code is too hard to test, that means your code is bad. The solution to every problem — no matter how hard the problem is — can be easy to test. If your program is not easy to test, then your code needs to be refactored or rewritten to make it easy. Doing that is how you learn to become a better programmer.
Zero-cost futures in Rust:
Google’s QUIC protocol: moving the web from TCP to UDP:
My skills are officially obsolete. I know HTTP 1.1 and TCP pretty well. I really need to understand the details of HTTP 2.0 and QUIC beyond the high-level architecture. I don't want to become a dinosaur who only knows UUCP or XNS. I've often wondered what it feels like to be an old, but still working programmer. This is probably part of it.
This is a wonderful guide on how to be a thoughtful collaborator. Except for the "Before you get hired" section, almost all of the advice applies to non-remote (local?) working as well.
Really cool library for working with time series in Python. See this Jupyter notebook for some compelling examples. I'm happy to see it works with Python 3 and is built on NumPy, SciPy, and Pandas.
What’s New in C# 7.0:
I surprisingly enjoyed using C# last year after ignoring it for years. It appears that the language is getting even more features with the next release. I don't think that's a good thing; some of what they're introducing seems overly complicated (e.g., out variable declarations).
"With two inputs, a neuron can classify the data points in two-dimensional space into two kinds with a straight line. If you have three inputs, a neuron can classify data points in three-dimensional space into two parts with a flat plane, and so on. This is called 'dividing n-dimensional space with a hyperplane.'"
— Understanding neural networks with TensorFlow Playground
"We were looking to make usage of Kafka and Python together just as fast as using Kafka from a JVM language. That’s what led us to develop the pykafka.rdkafka module. This is a Python C extension module that wraps the highly performant librdkafka client library written by Magnus Edenhill."
— PyKafka: Fast, Pythonic Kafka, at Last!
"To satisfy this claim, we need to see a complete set of statically checkable rules and a plausible argument that a program adhering to these rules cannot exhibit memory safety bugs. Notably, languages that offer memory safety are not just claiming you can write safe programs in the language, nor that there is a static checker that finds most memory safety bugs; they are claiming that code written in that language (or the safe subset thereof) cannot exhibit memory safety bugs."
— "Safe C++ Subset" Is Vapourware
"For each mutant the tool runs the unit test suite; and if that suite fails, the mutant is said to have been killed. That's a good thing. If, on the other hand, a mutant passes the test suite, it is said to have survived. This is a bad thing."
— Mutation Testing
"This meant that literally everything was asynchronous: all file and network IO, all message passing, and any “synchronization” activities like rendezvousing with other asynchronous work. The resulting system was highly concurrent, responsive to user input, and scaled like the dickens. But as you can imagine, it also came with some fascinating challenges."
— Asynchronous Everything
A couple of months ago I stopped letting people book meetings with me on Mondays before noon. I used to get anxiety on Sunday nights because I'd worry about preparing for my meetings the next day. Now I wake up Monday, have multiple hours to better prepare for the week, and my Sundays are as lazy as they should be.
Open Science Framework: "A free web app that supports project management and collaboration, connects services across the research lifecycle, and archives data, materials, and other research objects for private use or public sharing"
Landauer bounds: "the lower theoretical limit of energy consumption of computation"
"The new shared memory type, called SharedArrayBuffer, is very similar to the existing ArrayBuffer type; the main difference is that the memory represented by a SharedArrayBuffer can be referenced from multiple agents at the same time. (An agent is either the web page’s main program or one of its web workers.) The sharing is created by transferring the SharedArrayBuffer from one agent to another using postMessage..."
"Compared to OS-provided locks like pthread_mutex, WTF::Lock is 64 times smaller and up to 180 times faster. Compared to OS-provided condition variables like pthread_cond, WTF::Condition is 64 times smaller. Using WTF::Lock instead of pthread_mutex means that WebKit is 10% faster on JetStream, 5% faster on Speedometer, and 5% faster on our page loading test."
— Locking in WebKit
"uvloop makes asyncio fast. In fact, it is at least 2x faster than nodejs, gevent, as well as any other Python asynchronous framework. The performance of uvloop-based asyncio is close to that of Go programs."
— uvloop: Blazing fast Python networking
"The bias-variance trade-off appears in a lot of different areas of machine learning. All algorithms can be considered to have a certain degree of flexibility and this is certainly not specific to kNN. The goal of finding the sweet spot of flexibility that describes the patterns in the data well but is still generalizable to new data applies to basically all algorithms."
— Misleading modelling: overfitting, cross-validation, and the bias-variance trade-off
I've been building software professionally for over 10 years now. I love what I do and I hope to be an old programmer someday. But along the way, I've encountered many terrible things that have made me hate my job. I wish that someone had given me a roadmap of what to expect earlier in my career, so when some new and unfortunate awfulness occurred that I wouldn't have felt so alone and frustrated.
This post is meant to be such a guide. I have three goals.
The first goal is to look back: To identify experiences we both may have had in the past. These will help us establish some common ground of understanding. They'll serve as reference points to judge other unfamiliar problems.
The second goal is to look forward: To identify new issues that you may have not experienced yet, but likely could in the future depending on your path. I hope these items will help you prepare for what's coming and decide for yourself what's worth pursuing.
The third goal is to help you empathize and have mutual respect for the difficulties your teammates are facing. You may never endure many of the forward looking-items, especially if you're not a tech lead or manager. Similarly, if you are a tech lead or manager, you may have forgotten what it feels like to be an individual contributor; you may be out of touch with the day-to-day realities. I want to help everyone get on the same page.
The lists in the sections below are not in order of priority. They include observations that other people have told me about; they're not necessarily things I've experienced directly. So if you and I have worked together in the past, please don't assume that a particular example is about you. It's amazing how common these stories are.
It's also important to note that there are other categories of horrible things that this post does not confront at all: racism, sexism, ageism, aggression, and many other factors that contribute to a hostile work environment. I'm not qualified to write about these topics, and they've been described and analyzed thoughtfully elsewhere.
My objective in writing this post is to enumerate what follows from the nature of building software in teams. If you think I missed anything, please let me know. I can imagine that many of these points, especially in the lead and manager lists, also apply to other disciplines. And please keep in mind that these roles aren't all bad; my next post on this subject will be about the good things.
What's awful about being a software engineer?
For an individual contributor who writes code and is directed by a tech lead or manager.
There's just too much to learn and not enough time
The code is poorly written
The current abstractions are bad
I would have done this differently
The comments don't make any sense, aren't up-to-date
No documentation about how something was built or why it works this way
The build is slow
The tests are slow
The tests are flaky
There are no tests
Bad frameworks that require a lot of boilerplate, complex code, or confusing tests
Managers want me to sacrifice code quality for development speed
Dependencies change without notice
Differences between local dev, testing, and production
Getting ratholed on a problem or debugging for a long time
Broken or flaky tests that I need to modify but didn't write originally
Bugs or production issues that I have to deal with that other people caused, but they aren't actively trying to fix right now
Having to maintain someone else's crappy code or systems after they leave
Things that aren't automated that should be
Getting interrupted constantly by teammates and my manager
Context switching costs
My manager asks me to work on emergency projects
In code reviews my teammates are assholes and it feels like a personal attack
Other people are late in delivering the functionality that I need to do my job
Other engineers build their features or components too slowly
I have to wait for other people a lot
There are product decisions that I don't agree with
I feel like I'm just getting told what to do
Nobody respects my opinion
I work my ass off and then someone tells me to redo it
Product managers change requirements on me because they're overly reactive to criticism or feedback from other people
What's awful about being a tech lead?
For a software engineer who writes code and also leads the design and implementation work of a small group of individual contributors (who are managed by someone else).
Everything in production is broken all the time
Too many emails or documents to read and respond to
Work slips through the cracks
Falling behind on everything
Other people are making technical design decisions that I don't agree with, but I don't have the ability or authority to convince them to change their minds
Implementations that are sloppy or ignore existing best practices
Things coming up that I didn't plan for; late feature requirements that break my assumptions
I can get really stressed out about deadlines and dependencies, which makes it hard to unwind when I'm home from work
Everyone needs more supervision than I expect, no matter how hard I try to explain the details or document the plan
Launching something publicly takes forever and is blocked for bullshit non-technical reasons
Making the difficult choice between time and quality; deliberately shipping known bugs to production
I'm being responsible, why isn't everyone else?
I'm falling behind on my responsibilities and nobody is helping me
I don't understand what my manager does all day, but I don't think it's useful
I don't understand what the product managers do all day, but I don't think it adds value
It feels like other engineers on my team are trying to undermine me by not following the plan we already agreed to; I feel like a tattletale when I talk to their managers about it
Projects I thought would be my responsibility were taken away from me and given to someone else for reasons I don't understand
I don't have enough engineers working on my project to get the work done in a reasonable amount of time
People don't listen when I say how hard something will be and they're unwilling to reduce scope
What's awful about being an engineering manager?
For someone who manages a group of software engineers. This person may also be the tech lead, or manage tech leads who direct their reports.
It's hard to ask or tell people what to do without feeling like an asshole
It feels like everything is an emergency all the time
It feels like everyone is always complaining to me all day long
I have zero time for email
I have zero time for chit chat, even though I feel like an asshole for not being more social
When I get home I feel beaten up; sometimes it can be too much; if my significant other or people close to me are having issues they want to talk about, I can be so burnt out by the time I leave work that I'm unable to listen to their problems anymore.
At all times, some number of my reports are in one or more of these states:
About to quit
Upset at someone else on my team
Upset at someone else on another team
Upset with me
Offended by someone for good reason
Offended by someone for no good reason
Unhappy with the codebase for legitimate reasons
Unhappy with the codebase for perfectionist / invalid reasons
Unhappy with their project and want to work on something else, even though what they were doing is the most important thing
Having personal issues that are affecting their well-being, often causing them to have a negative effect on the morale of those around them
Bored; clear they'd take a new job if the right one was offered to them
Other managers do work by scheduling meetings. They can't write code; their only way to influence things is to talk. So I'm pulled into a bunch of useless meetings. And they almost always feel like a waste of time.
Writing less code sucks; it feels like I'm losing my edge. Sometimes it's hard to see how I'm contributing. I have to change my perspective on what I value. Finding satisfaction in helping others become more productive feels unnatural.
I'm going to miss making an important technical decision and things will go terribly wrong
A project is going to fall behind or fail because I delegated it to the wrong person
It feels like other managers are trying to undermine me with politics
My biggest problems are confidential and I can't ask for support or advice from anyone
It's unclear what the CTO/VP of engineering does; they don't seem to add any value; they ask ignorant questions and are generally disrespectful
Some of my best engineers are wasting their time on projects that don't matter, but I don't want to stop them from doing it in fear that it'll push them away from the team and lead them to quit
Everyone disagrees with at least some part of how I'm managing the team
"Anyone who interacts with process has a choice. You can either blindly follow the bulleted lists or you can ask why. They’re going to ignore you the first time you ask, the second time, too. The seventh time you will be labeled a troublemaker and you will run the risk of being uninvited to meetings, but I say keep asking why. Ask in a way that illuminates and doesn’t accuse. Listen hard when they attempt to explain and bumble it a bit because maybe they only know a bit of the origin story."
— The Process Myth (2013)
"You can think of this functionality of triggering computation based on database changes as being analogous to the trigger and materialized view functionality built into databases but instead of being limited to a single database and implemented only in PL/SQL, it operates at datacenter scale and can work with any data source."
— Introducing Kafka Streams: Stream Processing Made Simple
"Consider being lost in an endless desert. If you see an oasis in the distance, you head towards it even if the water is brackish and has camel dung floating in it. Bernstein et al are the oasis (or perhaps the mirage of an oasis), in an endless desert of cryptosystems and implementations of cryptosystems that keep breaking. So the (pending) Bernstein monoculture isn't necessarily a vote for Dan, it's more a vote against everything else."
— On the Impending Crypto Monoculture
"If you follow the tips in the official porting howto, then you can port your code file by file and simply do it slowly so that the problem at least stops growing for you."
— How to pitch Python 3 to management
"We can change the way people debug software, and in its own way that may be as important as my Web platform work, and it's work I desperately want to do."
— Leaving Mozilla (to work on rr)
"Why do people sometimes discuss "the stack" like it's some kind of revered fundamental object?"
— What is "the stack"?
"As a result, investors will change their lens from focusing solely on revenues and growth to also look at unit economics and burn rate. Founders will begin to make changes in core operating principles and resource allocation that might impact the lives of hundreds or even thousands of dedicated employees, vendors and customers. And ultimately, stronger companies will result. Don’t get me wrong, evolving from a unicorn into a cockroach will be extremely painful — but just like Mark Watney on Mars, the sooner you realize the situation on the ground has changed, the more time you have to “science the shit out of the problem” and succeed."
— First Round Capital's letter to their Limited Partners
"At a high level, there are two reasons why you might want a lock in a distributed application: for efficiency or for correctness."
— How to do distributed locking
"The Simpsons is one of greatest television comedies of all time and we hope that having ready access to the perfect screenshot will make people laugh and remind them to rewatch their favorite episodes."
— Frinkiac - The Simpsons Screenshot Search Engine
"I could easily imagine writing a program that calls the LLVM AST construction functions until it's described a program, and then directs LLVM to emit an exe. I would basically be writing software with no compiler."
— No Compiler
I recently bought and read most of the new Go book. I was excited to see that Kernighan, of K&R C, is one of the authors. It seems fitting to have someone from Bell Labs involved, given Go's roots. The other author, Donovan, is an engineer at Google (where the language was created).
Overall: It's a great book. Even though I'm already quite familiar with Go, I found reading the book to be a good use of my time. Practical knowledge I have from using Go was reinforced with a deeper level of understanding thanks to the book. I expected a lot, given the authors' pedigree, and the book lived up to it.
Some key points for potential readers:
Not for first-time programmers. This is an introduction to Go, not to programming.
Contains well-written, short, clear example code that motivates the information being presented.
Provides realistic exercises at the end of each section to help you practice.
Goes into important detail and subtlety without reading like a language specification.
Here's one example of the type of detail in the book that I really appreciate:
Before we go further, we should explain one subtlety in what it means for a type to have a method. Recall from Section 6.2 that for each named concrete type T , some of its methods have a receiver of type T itself whereas others require a *T pointer. Recall also that it is legal to call a *T method on an argument of type T so long as the argument is a variable; the compiler implicitly takes its address. But this is mere syntactic sugar: a value of type T does not possess all the methods that a *T pointer does, and as a result it might satisfy fewer interfaces.
And here's another:
In this respect, interface types are unusual. Other types are either safely comparable (like basic types and pointers) or not comparable at all (like slices, maps, and functions), but when comparing interface values or aggregate types that contain interface values, we must be aware of the potential for a panic. A similar risk exists when using interfaces as map keys or switch operands. Only compare interface values if you are certain that they contain dynamic values of comparable types.
This type of precise and succinct description is so valuable. In this book, such prose is almost always paired with a code example illustrating the point, in case you don't get it. This level of rigor you won't find in a beginner book. More advanced books, guides, and blog posts sometimes have this detail, but it's usually too verbose or sloppy to be clear. Well done!
In conclusion: If you want to improve your Go, this book is worth checking out.
When you're trying to solve a technical problem (writing code, analyzing data, designing systems), it's easy to decide that your work is done as soon as you verify that your results are valid (with tests, example cases, etc). But in practice, I find that being "done" only marks the beginning of the most important and most challenging phase: successfully explaining your solution to someone else.
While I'm in the zone, I can too easily convince myself that I've got a problem completely figured out. Later, I'll be explaining my ideas to someone else (in a meeting, document, code review, etc) and surprise myself by tripping over words, using bad analogies, and going into extraneous detail. I realize that I'm not making sense. The reality is that I can't explain my thoughts. My solution may as well not exist.
In my experience, the only way to make the act of explaining easier is to simplify the concept you're trying to explain. Simplifying is a skill that takes practice. You have to ask yourself, why can't a solution that's described in 500 words (or lines of code) be reduced to 250 or 100? You need to identify concrete reasons for why it can't be any shorter (e.g., nuance, abstraction, edge cases). You must ruthlessly cut things down as much as possible.
There are a few habits I've tried to cultivate to get better at simplifying:
When I get some new code working, I assume that I've only made it to the half-way point. I expect that refactoring and code review will take half of the overall time.
Similarly, when I'm writing (design documents, emails, etc), I expect that editing will require at least 50% of my time.
When someone else tells me their solution to a problem, I (annoyingly) say "let me repeat that back to you" and attempt to explain what they told me in different terms. It's amazing how often I'm wrong.
People too often assume that it takes more effort to have more (more words, more code, more features, etc). Adding more is actually the easy part. The hard part is having less in the end. The hard part is editing, reducing, refactoring, boiling it down.
What if the programmers who prefer functional languages are essentially "left-handed"? Some product of nature and nurture leads most programmers in the world to be "right-handed", preferring imperative programming languages over functional ones. Some especially gifted people are "ambidextrous" and work well with both types depending on the situation.
What if the choice between imperative and functional languages is purely subjective? They are two different perspectives on accomplishing the same thing. There are advantages and disadvantages to both. Neither approach is empirically better. One is much more common for unclear reasons. Programmers of either preference can't quite understand the appeal of the other side.
I'll leave it at that. And for the record: I'm also right-handed.
I've been making a game this holiday break. The big surprises so far: 1) Unity is an incredible tool (see screenshot below), 2) I like C# more than Java. The game simulates the physics of orbits. I'll report back when I've made something playable!
I enjoyed this post about one team's transition from Scala to Go. I think it's about how discouraging cleverness is important in programming languages and codebases. I don't think this article is about Go. The point here is the Scala language (and a popular library) made it possible for the code to be opaque to his own teammates (who were also Scala programmers!). Choosing to use Go going forward is one possible conclusion. If the article said that they chose to use vanilla Java 8 instead, I think the takeaway would be the same.
This week is my 10 year anniversary of working as a professional software engineer. The advice I've followed is "always be the worst player in the band". But over time the meaning of that statement has changed for me. I've realized that there's a lot more to making music than playing the notes.
Speaking of that Jazz book: I asked a musician I know for some advice on how to learn. He said, "I know just the book for you". He showed me "Jazz Theory" and I asked, "This looks great, but are there any other books I should read?" The musician's answer was, "No. If you wanted to learn about Jesus, you'd read the Bible. If you want to learn about Jazz, this is the book."
Why isn't there such an obvious go-to book in the realm of programming? I wonder who would disagree with my musician friend, why, and what book they'd recommend instead. Perhaps it's just the nature of learning: While you're inexperienced, people have amazing, definitive advice; once you know, everything is murky.
If you haven't heard of Kerbal Space Program, the gist of the game is this: You play the role of NASA or SpaceX to design, finance, build, launch, and control rockets, spaceships, robots, and astronauts that explore the solar system. It sounds like a great idea that ends up being boring in practice, but they somehow figured it out — Kerbal is extremely fun.
I've been watching the game evolve since it debuted. I'm really cautious about playing games because I get obsessed and they take up all my free time (to the detriment of other things, like open source projects and my social life). But this summer I was looking for a new way to chill out. I decided it was finally the right moment to start playing.
Perfectly matching a satellite's orbital parameters
It scratches a similar itch to Sim City and Civilization. But it also feels like Minecraft in how creative and arbitrary the game is. I haven't even begun to explore the tremendous Kerbal mod community that exists.
Here are some screenshots from my most recent accomplishment in the game: going to Jupiter ("Jool"), landing on two of its moons ("Bop" and "Pol"), and making it back to Earth ("Kerbin").
I had to fly a big spaceship that's powered by a nuclear rocket. Look how stoked those Kerbals are, in the lower right of the screen, to be zooming off for a 10 year mission to the edge of the solar system:
Here I'm using the guidance computer to execute my burn. If you haven't played the game this will look insane. For someone who plays the game this is actually a trivial example:
And finally, here's what it looks like when the astronauts ("Kerbonauts") make it back home safely. Succeeding in getting them home is one of the best parts. (This was a night landing because I ran out of fuel and had to rely on aerobraking to slow down).
I've barely scratched the surface of this game. I can't recommend it enough. But don't blame me if it ruins your life, too.
This article about SpaceX and their goal of colonizing Mars is the most inspiring thing I've read all year. It's long but totally worth getting through. It includes tons of great links, such as these travel posters:
I saw some giant sequoias a few weeks ago. There aren't many of these 2000+ year old trees left. Growing more seems extremely difficult given all of the war, fire, and suffering that occurs on Earth during such a time period. With all our technology and sophistication we can send a probe to Pluto, but it seems impossible to grow a tree into old age. It's so simple, yet so difficult.
Time is not something we can control. Or is it? You can imagine launching baby trees into space and shooting them off towards the speed of light. Time would elapse faster for the tree than on earth due to special relativity. After a large orbit, the trees would return and be thousands of years older than when they left. Land them back on Earth, put them in the ground: problem solved.
I’m happy to report that we at Mozilla have started working with Chromium, Edge and WebKit engineers on creating a new standard, WebAssembly, that defines a portable, size- and load-time-efficient format and execution model specifically designed to serve as a compilation target for the Web.
Python 2.7.11 will be 15-20% faster by using computed gotos instead of a switch statement, thanks to a patch from Intel. Edit: To be clear, it's the bytecode interpreter's code that's faster; overall benchmarks are here.