Being productive with your tools

Mag­num is de­vel­oped with a “Zen Gar­den” phi­los­o­phy in mind, fo­cus­ing on pro­duc­tiv­i­ty, pre­dictabil­i­ty and ease of use. Let’s see how can that ex­tend be­yond just the li­brary it­self — in­to your dai­ly work­flow.

Trig­gered by an (ad­mit­ted­ly very harsh) re­cent ar­ti­cle by @cliff­s­ki, a re­cent need to re­boot in­to Win­dows 10 in or­der to fix MSVC 2019 sup­port in Mag­num (to be hor­ri­fied from the UX of Win­dows 10), and a re­sult­ing dis­cus­sion on our Git­ter chat, I de­cid­ed to share what I think is use­ful for be­ing a pro­duc­tive de­vel­op­er. In this case with Lin­ux and the KDE Plas­ma 5 desk­top be­ing my sys­tem of choice.

Git is a friend, not a de­spised en­e­my

Think of Git not as of the an­noy­ing ob­scure last-mile tool to “push your code to cus­tomers”, but a tool that can ac­tive­ly keep you fo­cused, or­ga­nized, help you re­view your changes, have your ed­it his­to­ry safe and let you eas­i­ly pick up in­ter­rupt­ed work from yes­ter­day.

I’m reg­u­lar­ly us­ing git status and git diff as a “scope guard” — to avoid the set of changes in my work­ing copy grow­ing too large. Then I’m us­ing the in­ter­ac­tive git add -i to pick rel­e­vant changes and com­mit them in­to as many sep­a­rate com­mits as de­sir­able, of­ten us­ing git re­base -i to back-in­te­grate fix­up patch­es to ex­ist­ing com­mits.

There’s noth­ing eas­i­er than do­ing a quick git stash to see if your lat­est mod­i­fi­ca­tions are to blame for bro­ken tests or if it’s some­thing else.

Some peo­ple ar­gue that “[their] job is cod­ing, not mak­ing beau­ti­ful com­mit logs”, but from my ex­pe­ri­ence scop­ing up a task in­to a se­quence of clean com­mits helps a lot — if the diff is self-con­tained, makes sense and doesn’t con­tain un­re­lat­ed changes, you can re­view your work much eas­i­er and be sure you didn’t mess any­thing up. It pays off lat­er when in­ves­ti­gat­ing bugs as well, git blame to­geth­er with git bisect are pow­er­ful tools for pin­ning down is­sues in large or un­fa­mil­iar code­bas­es. But they need a clean his­to­ry to work prop­er­ly.

Get to know your ed­i­tor, shell and key­board lay­out

While ev­ery ed­i­tor is dif­fer­ent, there are a few fea­tures I ab­so­lute­ly de­mand to con­sid­er it us­able. If the ed­i­tor starts slow, has a typ­ing lag, can’t ap­ply a con­sis­tent in­den­ta­tion and white­space pol­i­cy, doesn’t know block edit­ing or com­ment­ing/un­com­ment­ing code isn’t a key short­cut, then it’s use­less for me. Kate from KDE (and ev­ery­thing based on it — KWrite, KDe­vel­op) is my ed­i­tor of choice. I have to ad­mit I nev­er both­ered to learn Vim — most­ly be­cause the cur­rent way I ed­it files is fast enough, the bot­tle­neck be­ing my brain and not “how fast I can type”.

Know­ing how to nav­i­gate with key­board is es­sen­tial as well — far too of­ten I see se­nior pro­gram­mers with 20+ years of ex­pe­ri­ence mov­ing across words or lines on­ly by re­peat­ed­ly press­ing ar­row keys like if there was no oth­er way. Ctrl Ar­row skips whole words and there’s Home / End, use them! On Mac, how­ev­er, I was nev­er able to have those work con­sis­tent­ly across apps and it’s one of rea­sons why I’d rather SSH in­to a Mac than use its key­board di­rect­ly.

Pick a shell where you can work fast — 90% of what you do there is re­peat­ed­ly ex­e­cut­ing short com­mands, so it’s es­sen­tial to have this op­ti­mized. The plain Win­dows cmd.exe is ab­so­lute­ly ter­ri­ble at that, but most oth­er shells can be made a joy to work with if you con­fig­ure their his­to­ry han­dling.

"\e[A":history-search-backward
"\e[B":history-search-forward

Paste this in­to your /etc/inputrc, open a new ter­mi­nal, type a com­mand pre­fix and pres Up to au­to­com­plete it from typed his­to­ry. Sim­ple and in­tu­itive. You can thank me lat­er.

Have your desk­top work for you

I have a sin­gle ex­ter­nal 27” 4K screen. It prac­ti­cal­ly cov­ers my field of view and fits ev­ery­thing I can fo­cus on in a sin­gle mo­ment, but not more. By choice — I cur­rent­ly don’t have any de­sire to use more than one screen. What I usu­al­ly see else­where is peo­ple hav­ing one screen with an IDE and the oth­er with a sin­gle Slack win­dow, an e-mail or a brows­er, with all the mes­sages, an­i­mat­ed ads and no­ti­fi­ca­tions con­stant­ly beg­ging for at­ten­tion, on­ly mak­ing fo­cused work mis­er­able.

What I have in­stead is six vir­tu­al desk­tops — one ded­i­cat­ed for the brows­er, an­oth­er three for work, one desk­top sole­ly for file / pack­age man­age­ment and one with a mu­sic play­er. This makes it pos­si­ble to have each vir­tu­al desk­top a sin­gle area of fo­cus, com­pared to a pile of win­dows the IDE won’t dis­tract you when read­ing a PDF and a brows­er win­dow won’t dis­tract from cod­ing. It’s im­por­tant that the taskbar shows on­ly win­dows from the cur­rent desk­top and noth­ing else. Re­cent­ly I even turned off all brows­er push no­ti­fi­ca­tions so ac­tiv­i­ty from one vir­tu­al desk­top doesn’t leak in­to oth­ers in any way.

Desk­top switch­er in the taskbar
Ctrl F1F6 short­cuts for switch­ing. Ctrl F1 is al­ways the brows­er desk­top.

Im­por­tant for desk­top switch­ing short­cuts is that they’re ab­so­lute (so I don’t have to think about di­rec­tion, just the des­ti­na­tion) and that there’s no an­i­ma­tion — if the brain is fo­cused on a par­tic­u­lar screen area, quick switch­ing to an­oth­er desk­top and back will not cause it to lose con­text. That’s al­so why I nev­er use Alt Tab, it has an un­pre­dictable or­der and caus­es so much vis­ual noise that los­ing con­text is in­evitable. An­oth­er es­sen­tial fea­ture is an abil­i­ty to make a win­dow stay al­ways on top or be present on all vir­tu­al desk­tops — a float­ing con­sole win­dow with a long-run­ning op­er­a­tion, for ex­am­ple.

Al­ways on top
A diff opened in gitk stays on top while edit­ing code in a fullscreen IDE be­low; a “rolled-up” con­sole win­dow with a long-run­ning op­er­a­tion above it.

Your com­put­er can be a pow­er-house

It’s com­mon for me to have a brows­er with 100+ tabs open, two IDEs with ~50 files each, sev­er­al con­sole win­dows each with mul­ti­ple tabs, file man­ag­er with five split tabs, a dozen of PDFs open on top and a spread­sheet for pro­cras­ti­nat­ing on my tax­es. When I fin­ish my work, I put the lap­top to sleep and when I re­sume work the next day, it’s all there, ex­act­ly how I left it. Up­time of 90 days isn’t any­thing ex­tra­or­di­nary ei­ther.

A lap­top with 16 GB of RAM, of­ten run­ning on­ly at 800 MHz, has no prob­lem keep­ing up with all that. But it’s im­por­tant that I can re­ly on the sys­tem to not do any shady busi­ness in the back­ground — hog­ging the CPU with an an­tivirus check or down­load­ing gi­ga­bytes of sys­tem up­dates un­less I tell it to (and then ran­dom­ly re­boot­ing) would be an ab­so­lute show­stop­per.

Lit­tle Big Things

On KDE Plas­ma, if I press Alt F2, KRun­ner, a pop­up search win­dow, ap­pears. It can open apps, search tabs in my brows­er, do sim­ple cal­cu­la­tions but al­so has a plug­in that gives me ac­cess to a data­base of pre-de­fined sym­bols — whether I need an em-dash for a tweet, a trade­mark char­ac­ter or a ¯\_(ツ)_/¯ re­sponse for a chat. A crit­i­cal re­quire­ment is that it has to work pre­dictably and with­out any de­lay; typ­ing a known pre­fix and press­ing En­ter will al­ways give the same re­sult, no mat­ter how fast I type.

An­oth­er very handy thing is a glob­al key­board his­to­ry. More of­ten than not, you need to copy sev­er­al things at once, not just one. Or you ac­ci­den­tal­ly copy some­thing and lose the pre­cious clip­board con­tents. Es­pe­cial­ly when you need to switch win­dows or desk­tops to copy mul­ti­ple things, the vis­ual noise will make your brain go out of the zone very quick­ly. With Klip­per I can use Ctrl Alt Up or Down to pick a dif­fer­ent en­try from the clip­board his­to­ry.

Python is the best cal­cu­la­tor, shell and knife

It’s a good idea to have a pen and a piece of pa­per on your desk, es­pe­cial­ly when you are cod­ing vis­ual things. Us­ing it to cal­cu­late a dot prod­uct by hand isn’t. A ter­mi­nal win­dow with an in­ter­ac­tive Python in­stance is a much bet­ter tool. And with Mag­num now get­ting Python bind­in­gs, it has ev­ery­thing need­ed.

>>> from magnum import *
>>> Matrix3.rotation(Deg(45))
Matrix(0.707107, -0.707107, 0,
       0.707107, 0.707107, 0,
       0, 0, 1)

Quick, where are the mi­nus signs in a 2D ro­ta­tion ma­trix?

Python is the go-to choice al­so for all string-pro­cess­ing shell scripts longer than one line — in­stead of try­ing to re­mem­ber how to use awk and cut in­side a while loop in Bash, whip that up in Python. It’ll be eas­i­er to de­bug, ex­tend and you wouldn’t need to learn the ob­scure tools again a week lat­er.

Fast it­er­a­tion times are key

There’s no worse pro­duc­tiv­i­ty killer than a tool that makes me wait un­til an op­er­a­tion is done. That’s a forced in­ter­rup­tion and my brain im­me­di­ate­ly gives up on all con­text. I can it­er­ate or core APIs in Mag­num ba­si­cal­ly with­out in­ter­rup­tion, in­cre­men­tal com­pi­la­tion tak­ing few sec­onds at most. Then, with Util­i­ty::Tweak­able, I can live-ed­it con­stants in a run­ning app for even faster turn­around times.

In con­trast, Mag­num’s Python bind­in­gs are done with py­bind11, which ex­pos­es a very sim­ple API do­ing very com­plex things un­der­neath. How­ev­er I soon got in­to a state where the it­er­a­tion time of a sin­gle com­pile & link got to al­most a minute — the whole en­gine with 800 tar­gets com­piles from scratch faster than that. To stay oc­cu­pied dur­ing this “down­time”, I tem­po­rar­ily switch to an­oth­er task, but the con­text switch over­head slow­ly makes the fo­cus dis­ap­pear.

Have a stack you can trust …

With Mag­num not far from be­ing a decade old, I have the lux­u­ry of re­ly­ing on a ma­ture, sta­ble and well-test­ed code­base. De­vel­op­ing new things on top of a trust­ed stack is a breeze, be­cause com­bin­ing well-test­ed and well-un­der­stood build­ing blocks most of­ten leads to the re­sult be­hav­ing cor­rect­ly as well — with any de­bug­ging hap­pen­ing on­ly on the sur­face lev­el.

This ex­tends to pro­vid­ing sup­port as well — know­ing the in­ter­nals well I can quick­ly nar­row down a re­port­ed prob­lem, re­mote­ly di­ag­nose it by ask­ing just a few ques­tions and pro­vide ei­ther a so­lu­tion or a work­around al­most im­me­di­ate­ly.

… and sev­er­al al­ter­na­tives for the stacks you can’t

Not ev­ery­thing is a “Zen Gar­den”, though — there’s the OS, GPU driv­ers, third par­ty li­braries, com­pil­ers and hard­ware, each at a var­i­ous state of sta­bil­i­ty. For those it’s im­por­tant to al­ways have an al­ter­na­tive im­ple­men­ta­tion to test on — if an im­age fails to load with one plug­in, try with an­oth­er. If a shad­er works flaw­less­ly on one GPU, it might as well crash and burn on an­oth­er.

Try to pri­mar­i­ly de­vel­op against the most con­form­ing im­ple­men­ta­tion (of a com­pil­er, stan­dard li­brary, GPU driv­er, file for­mat load­er) and reg­u­lar­ly test on at least one oth­er, to ver­i­fy your as­sump­tions. In­vest­ing a week (or even a month) of your time in­to set­ting up a CI test ma­trix that does au­to­mat­ic test­ing for you on sev­er­al dif­fer­ent plat­forms, ide­al­ly in­clud­ing GPU code, will pay back mul­ti­ple times.

Build Ma­trix
And that’s just the top half.

Web is un­for­tu­nate­ly just too damn slow

Ev­er since I made the light­weight Mag­num web­site and docs, the rest of the In­ter­net com­par­a­tive­ly start­ed to feel much slow­er. While I can jump to a doc­u­men­ta­tion of Mesh­Tools::gen­er­ateS­mooth­Nor­mals() in a frac­tion of a sec­ond, nav­i­gat­ing to a par­tic­u­lar is­sue of a par­tic­u­lar project through the GitHub UI to write a re­ply is so slow that it’s faster for me to just re­call its num­ber and type the whole ad­dress out.

For ex­ter­nal li­braries I’m us­ing, I of­ten end up re­gen­er­at­ing the docs my­self us­ing m.css. The search func­tion­al­i­ty of any Sphinx-gen­er­at­ed docs is a bad joke and Googling the ac­tu­al be­hav­ior of Python’s splitlines() isn’t near­ly as straight­for­ward as it should be ei­ther. I end­ed up build­ing my own search­able copy of Eigen doc­u­men­ta­tion, did a sim­i­lar thing for An­droid NDK and I’m plan­ning to do that for Python stan­dard li­brary as well.