Skip to content

Instantly share code, notes, and snippets.

@bb010g
Last active October 31, 2017 07:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bb010g/ca0b8143eb6054831e240ad1e7dec9a1 to your computer and use it in GitHub Desktop.
Save bb010g/ca0b8143eb6054831e240ad1e7dec9a1 to your computer and use it in GitHub Desktop.
Ramblings on comments and libraries

Comments are for explaining non-obvious algorithms. If your function has a strange signature that doesn't make it's basic utility obvious, it should be renamed and the interface possibly rethought. If it's just a weird algorithm, document the crap out of it in documentation comments, not normal comments. If your internal code needs comments to be readable, refactor until the flow is understandable. If someone is left wondering why you did something a certain way, comment why. (A good sign for this is if it took you a while to come up with that piece of code.) If someone is left wondering what your code even does, rewrite it.

Your code should be written for someone who has reasonable knowledge of the language. If you're consistently using fringe features, it's probably a good idea to leave some notes and places to look for documentation around the root of the project, or even in the readme. Don't use fancy language features for their own sake; they should only serve to enhance the clarity of your code over their equivalent implementations.

Also, kinda tying into those last points, for the love of all that is good in the world, DO NOT OVER-ENGINEER YOUR CODE. Build for what you know you need and can actively test. You can get on an abstraction high sometimes and build something that's theoretically nice, but practically unusable. When you need more power, think about why you need it, and then start implimenting with that in mind. A good example of this in practice is the Piston project in Rust. They produced some really cool stuff, but it was hard to use, and they're currently reimplementing their core graphics library to better fit real world use cases (http://gfx-rs.github.io/2017/07/24/low-level.html, https://users.rust-lang.org/t/its-gfx-rs-all-the-way-down/13339).

This doesn't mean to write code that smells, but keep in mind the maintenance cost of every abstraction you have to maintain. It's easier to heavily refactor when there's less to refactor. A good piece on this is Paul Graham's "Do Things That Don't Scale", which covers it from a more startup/business oriented perspective. From my experience over time writing libraries, I just really don't know where something should go until I have a use case. As you grow more experienced, you'll be able to predict what should work more accurately and be a bit more adventurous in your steps, but don't let the architecture drive the application. If you're writing or maintaining a library, use it. That experience is invaluable, and you'll also catch stuff like performance bugs more often. As for how this applies to more "throwaway" code bases such as FRC, try to think about what you can save. Layers of abstraction are powerful. RobotPy is your base, but you can build components of various re-usability on top of that. Your goal is to have your main competition functions and parameters adjustable in a way that doesn't make you want to claw your eyes out. A better, but less important, goal is to have that be kinda fun to write. If something's being reused a lot (whether it's copy/paste, functions, classes, similar data structures, whatever), take some time when not much is required of you to think about why it's being reused. Maybe sketch some stuff out. If you can figure out a nice API to build the high-level functionality of your bot from, you'll make your life going forward a lot easier. It won't be perfect, and you'll probably end up refactoring that API a lot, or maybe even rewriting it, but in the end you'll have a clearer understanding of what's going on, why your code is the way it is, and probably be able to reuse it next year in a way that doesn't involve divining why removing this one line breaks half the bot.

Circling back to documentation, public API-level documentation comments are amazingly important. Yes, they take time to write, but for your consumers (whether they're someone else or you in a week), they make everything a lot easier to work with. You can figure out a function pretty easily with a good signature and good docs, but it's a lot more effort to go read the source and figure out why. (A lot of that is having to load the relevant code into your head to actually grok what's happening. Taken incorrectly, this natural consequence of just dropping into a non-trivial codebase at a random point may look like a reason for more code comments. Every piece of code will make you wonder a bit more when you first look at it. Calm down, poke around for a little while, and come back. The internals should be clearer.) However, good API documentation takes effort to maintain. This is where those levels of APIs come back in: after a little while, you'll notice that you'll be changing some code less and less. This means that you should probably go document it while it's relatively fresh in your head, but not going to make you rewrite everything tomorrow when you decide that your old design was trash. You can then experiment on top of these new "libraries" and not have to think about what's going on inside, freeing up your brain for thinking about the problem by interacting with meaningful, well-documented, high-level functions and data structures.

As Bartoz says in his wonderful Category Theory for Programmers (also available in PDF), composition is the essence of programming. Composable, higher level pieces allow us to stop thinking about what's going on underneath and free up brain space for what we want to accomplish. If you're building something that can't compose, keep in mind that you'll probably have to rewrite it at some point to keep it from devolving into spaghetti as you want to push it further. (UIs are a good example of this being fine: you know you're at the top, and can get away with not being as composable as the average library a lot of the time.) Documentation comments are what allow your users to have confidence in what their using and avoid spending extra brain power on the details.

Note that effective composability relies on these pieces fitting together tightly: you don't want these things to be brittle and snap if you bend the end result the wrong way. All non-trivial abstractions, to some degree, are leaky. (Go read Joel Spolsky's "The Law of Leaky Abstractions" if you haven't yet.) A benefit of working with your abstraction as you're developing it is that your tend to take smaller leaps. You find nice, functional steps, and are able to document them, along with the generally few quirks (leaks) they have. By keeping each steps' size down, you make keeping track of leakiness reasonable, and make it possible to build new abstractions on top. It's hard to build a good structure on top of a really faulty foundation.

So overall, keep your code composable, please please please write good documentation for your public APIs, write comments about non-obvious algorithms or decisions, and use what you make every step of the way. (If you're maintaining, you don't have to use it as much, but it's good to keep active somewhat so you can see where others' concerns are coming from.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment