What functional programming and secure code have in common

As a programmer, especially a strong-typing functional programmer, I love abstraction. Defining what the program does and expressing that cleanly in code. The code should express little else.

Now I’m studying secure code, and it is the opposite. It’s all about what can happen, not what should. So many nitty-gritty details of network protocols, memory layout, old versions of libraries, operating system flukes. There is no abstraction here, only gritty reality.

Functional programming says, bad things can’t happen. Secure code says, bad things do happen.

In this sense they have the same goal!

Functional programmers use property-based testing to establish boundaries for what the code can output. Security people (and hackers) use fuzz testing to surprise the running app, looking for new behavior. Fuzz testing and property-based testing are similar: both use programs to come up hundreds of random inputs and throw them at the software.

Functional programmers aim for referential transparency, no side effects. Languages like Haskell enforce this. Strong typing can imbue the code with magical mathematical properties: incorrect code doesn’t compile. Functional programmers express what the software should do, excluding many things it shouldn’t.

Security laughs at “shouldn’t.” Secure code also aims to exclude possibilities, by checking input, and checking again, and never quite believing in “can’t happen.”

Much of “quality” in software is everything it shouldn’t do.

We write acceptance tests as if the prescribed happy path is all we need. But it’s everything else that makes software rich and real. All the error help & guidance messages that push legit users back on to the happy path without informing them about technical underpinnings. Serving requests just enough, without serving so many for any one client that others suffer. Storing data that’s flexible enough for use, but not so flexible it surprises our software or admin tools. Letting the right people in without headaches. Informing the developers of the system what’s going on. And finally, everything the software shouldn’t do yet — support change.

Requirements define constraints. Quality is everywhere else.