Skip to content

Security consultancy from the other side

I used to run an application security consultancy business, back before the kinds of businesses who knew they needed to consider application security had got past assessing creating mobile apps. Whoops!

Something that occasionally, nay, often happened was that clients would get frustrated if I didn’t give them a direct answer to a question they asked, or if the answer to an apparently simple question was a one-day workshop. It certainly seems sketchy that someone who charges a day rate would rather engage in another day’s work than write a quick email, doesn’t it?

Today I was on the receiving end of this interaction. I’m defining the data encryption standards for our applications, so talked to our tame infosecnician about the problem. Of course, rather than specific recommendations about protocols, modes of operation, key lengths, rotation frequencies etc he would only answer my questions with other questions.

And this is totally normal and OK. The reason that I want to encrypt (some of) this data is that its confidentiality and integrity are valuable to me (my employer) and it’s worth investing some of my (our) time in protecting those attributes. How much is it worth? How much risk am I willing to leave on the table? How long should the data be protected for? Who do I trust with the data, and how much do I trust them?

Only I can answer those questions, so it’s not up to someone else to answer them for me, but they can remind me that I need to ask them.

Choose boring employers

Amusingly, my previous post choose boring employees was shared to hacker news under the off-by-one erroneous title choose boring employers. That seemed funny enough to run with, but what does it mean to choose boring employers?

One interpretation is that a boring employer is one where you do not live in interesting times. Where you can get on with your job, and with finding new and better ways to do your job, without constantly fighting fires.

But what if you’re happiest in an environment where you are fighting fires? In that case, you probably should surround yourself with arsonists.

Another interpretation is to invert the discussion in Choose Boring Employees: find an employer who spends their innovation tokens wisely. One who’s OK with the answer to “how do I store these tuples of known structure” being “in a relational database”, or one who doesn’t mind when the answer to “what platform should we base our whole business on” starting with “I skim-read a blog post on HN when I was riding MUNI this morning and…”.

But, let’s be clear, there’s a place for the shiny new technology. Sometimes you do need to spend your innovation tokens, so you don’t want to be somewhere that won’t let you do it at all. Working on a proof of concept, you want to get to proof quickly, so it may be time to throw caution to the wind (unless the concept you’re trying to prove involves working within some cautious boundaries). So boring need not get as far as frustrating.

Choose boring employees

An idea I’ve heard from many directions recently is that “we” (whoever they are) “need to be on the latest tech stack in order to attract developers”. And yes, you do attract developers that way. Developers who want to be paid to work on the latest technology.

Next year, your company will be a year more mature. Your product will be a year more developed. You will have a year more customers. You’ll have a year more tech debt to pay off.

And your cutting-edge tech stack will be so last year. Your employees will be looking at the new startup in the office next door, and how they’re hiring to work on the latest stack while you’re still on your 2017 legacy technology.

In which GNUstep confuses and ultimately disappoints

I’m not the most hardcore of GNUstep people, but I’m certainly somewhat invested. I’ve been building apps, lurking in lists, and contributing code on and off for around 13 years, including a job working with a few of the maintainers. I am trying to build the corpus of documentation intrinsic to GNUstep, i.e. that which doesn’t require interpretating Apple’s Cocoa docs through a compatibility lens.

So when I discovered that an app I need doesn’t exist and that I’d have to make it myself, GNUstep was the obvious choice. I could take all that experience and investment in Objective-C and Cocoa and build the app I need. I’d probably be able to contribute to the framework along the way, too: a previous side project of mine resulted in adding NSUUID to gnustep-base.

This time, however, the amount of framework support needed proved too great. The thing I want is an app to help me organise and read some PDFs, and there’s no PDF-reading support in GNUstep. Well, let me be more specific: there has been, but it’s disappeared. There was a library called PopplerKit that wraps the poppler PDF-rendering library. However the only references to that are to a subversion repo in the now-defunct Gna! project: i.e. there is no PopplerKit available any more.

One option I considered is to write my own poppler wrapper, perhaps exposing the same interface as Apple’s PDFKit. This would probably be welcome in the GNUstep community, would improve their libraries’ API compatibility with Cocoa, and be useful to other developers. However, it’s also a distraction from what I’m trying to do, which is to make my PDF app.

So I decided to choose a different technology, where PDF rendering is already available. Poppler has a Qt5 interface already, so I built my app using C++ and Qt5. After half an hour of effort I already have a page-turning PDF viewer, which is not pleasant but enough to let me be confident that I can build the rest of the application.

I had thought that this would be the end of the story. I would have a moral here (still a useful moral) about picking the technology that lets you get your task solved, being realistic about how much you’re drawn to your pet project or favourite “stack” and whether that bias is worth tipping the balance: in many cases, it probably is, but be aware of what it’s costing you. I expected to talk about code rot, and to despair over the fact that a library I knew about and was aware could solve my problem no longer existed.

Then, in preparing this post, I happened to dive through an unrelated repository on github, the GNUstep Applications Project (a repository containing a collection of unrelated applications), and discovered that it contains a PDFKit implementation. It doesn’t contain PDFView so can’t be used as a reader, so it doesn’t change the choice over which of GNUstep or Qt should be used now. But it does change the amount of effort required to reimplement PDFKit on top of GNUstep, should I revisit that in the future.

So there’s a point in here about discoverability too. I did quite a lot of searching to find that there isn’t a PopplerKit, and also found that there wasn’t a GNUstep PDFKit. Even so, there was, and even more searching turned it up. En route I found that a different technology had what I wanted, and changed my choice of programming language and application framework in order to use that technology in my project. You can’t merely build it and expect them to come, you have to show them that it’s there too.

On the “advances” in web development since 1995

The first “web application” I worked on was written in a late version of WebObjects, version 4.5. An HTTP request was handled by an “adaptor” layer that chose a controller based on the parameters of the request, you could call this routing if you like. The controller accesses the data model, any relevant application logic, and binds the results to the user interface. That user interface could be an HTML and JavaScript document for presentation in a browser, or it could be a structured document destined for consumption by a remote computer program. The structured document format would probably have looked something like this:

{
  "first_name": "Graham";
  "last_name": "Lee";
}

The data can be bound on properties in objects called “components” that represent different elements in an HTML document, like images, anchors, paragraphs and so on.

Different frameworks for interfacing with various data storage technologies, such as relational databases, were available.

And basically nothing – apart from the cost – has changed since then. A MEAN application works in the same way, but with the “advance” that it makes it easier to support single-page applications so you can send a few megabytes of transpiled bundle over an unreliable mobile data network to run on a slow, battery-operated smartphone processor instead of in the server. And the data storage interfaces have been expanded to include things that don’t work as well as the relational databases. Other “advances” include a complicated collection of transpilers and packagers that mean every project has its own value of “JavaScript”.

It’s not like anything has particularly got much easier, either. For example, a particular issue I’ve had to deal with in a React application – binding a presentation component to properties of an element in a collection – is exactly as difficult now as it was when WebObjects introduced WORepetition in 1995. Before we get to the point where it becomes easier, we’ll give up on React, and Node, and move on to whatever comes after. Then give up on that.

In software engineering, if something isn’t broke, fix it ’til it is.

When Object-Oriented Programming Isn’t

A problem I was investigating today led me to a two-line Ruby method much like this:

class App
  # ...
  def write_file_if_configured
    file_writer = FileWriter.new(@configuration.options)
    file_writer.write if file_writer.can_write?
  end
end

This method definitely looks nice and object-oriented, and satisfies many code quality rules: it’s shorter than 10 lines, contains no branches, no Boolean parameters (unless there are any hiding in that options object?), indeed no parameters at all.

It also conforms to the Law of Demeter: it calls a method on one of its fields, it creates a local object and calls methods on that objects, and it doesn’t send messages to any other object.

In fact there’s significant Feature Envy in this method. The method is much more interested in the FileWriter than in its own class, only passing along some data. Moreover, it’s not merely using the writer, it’s creating it too.

That means that there’s no way that this method can take advantage of polymorphism. The behaviour, all invoked on the second line, can only be performed by an instance of FileWriter, the classglobal variable invoked on the first line.

FileWriter has no independent existence, and therefore is not really an object. It’s a procedure (one that uses @configuration.options to discover whether a file can be written, and write that file if so), that’s been split into three procedure calls that must be invoked in sequence. There’s no encapsulation, because nothing is hidden: creation and use are all right there in one place. The App class is not open to extension because the extension point (the choice of writer object) is tightly coupled to the behaviour.

Unsurprisingly that makes this code harder to “reason about” (a fancy phrase meaning “grok”) than it could otherwise be, and that with no additional benefit coming from encapsulation or polymorphism. Once again, the failure of object-oriented programming is discovered to be that it hasn’t been tried.

Two ways of thinking

I’ve used this idea in conversations for years, and can’t find a post on it, which I find surprising but there you go. There are, broadly speaking, two different ways to look at programming languages. And I think that these mean two different ways to select programming languages, which are asymmetric. However, they can lead to the same choice, but viewed in different ways: if you use one of those languages then you need to understand that these different views exist to understand decisions made by programmers working in those languages.

On the one hand, the Language Lawyer seeks out languages with plenty of esoterica and specifics, in order to become proficient at recalling those esoterica and demonstrating their correct application. The Language Lawyer loves that reference and value types in swift are actually called class and struct, or that the difference between class and struct in C++ is the default member visibility, and that neither is actually related to the words class or struct. They are delighted both to know and to explain that in Perl 5, @var, \var and $var refer to the same variable but are accessed in different contexts and what they evaluate to in those contexts. They are excited to explain that technically the admonition that you must always return a value from a non-void function in C is not true, because since C99 if you have a function called main that returns control without returning a value, the compiler will implicitly return 0.

The Language Lawyer seeks out a language with such esoterica. But learning it is complex and time-consuming, so they probably don’t change particularly often. In fact, changing language may be deleterious, because then other people will know the esoterica that they get caught out on. Imagine knowing how the function overloading rules in C++ work, then trying something similar in Java and finding that you’re wrong. And that somebody else knows that. The shame! Once their language is chosen, the Language Lawyer will form heuristic rules of which language feature to use when solving which problem.

Standing in the other corner, the Lazy Evaluator wants to avoid those edge cases, because it makes them think about the programming language, not the software problem. They’d much prefer to have an environment in which there’s as much as one way to express their solution, then worry about how to express their solution using that one tool. The Lazy Evaluator loves Ruby because Everything Is An Object (and they love Io more because in Ruby there are also classes). The Lazy Evaluator is delighted to know that in Lisp, Everything Is A List. They are excited to be programming in Haskell, where Everything Is A Function.

Both of these people can be happy with the same programming language, though clearly this will be for different reasons, and their different thought processes will bring them into conflict. The Lazy Evaluator will be happy enough using the third-most common C-family language, C++– (full name: “C++98 but never use exceptions or templates, avoid multiple inheritance, and [never/always] mark methods as virtual”). The Language Lawyer will enjoy demonstrating const-correctness and the difference between l-, r-, pr-, gl- and x-values, but the two will come to blows over whether the Lazy Evaluator is denying the full elegant expressiveness of the language by proscribing exceptions.

Similarly, the Lazy Evaluator can look at JavaScript, see the simple classless object model with dynamic dispatch remembered from Self or Io, even notice that Functions Are Objects Too and work in that Everything Is An Object paradigm. The Language Lawyer can be confident that at some point, all of the weird coercion behaviour, the Which this Is That this Anyway question, and the You Need That Flag With This Polyfill To Get That Language Feature issues will come up. But the Lazy Evaluator will notice that That Flag also enables class syntax and arrow functions, we already have prototypes and function functions, and disagreement will ensue.

So in what way are those ways of thinking asymmetric? Invoking Hickey’s Corollary, you can never be recomplecting. It’s easier to compromise on C++ But Without Exceptions than it is to compromise on Lisp But We’ll Add Other Atom Types. You can choose JavaScript But Never Use this, you can’t choose Haskell But With Type-Escaping Side Effects.

This isn’t really about programming languages, it’s about any form of model or abstraction. Languages are a great example though. I think it’s a mindset thing. Your task is to solve this problem, using this tool. Are you focusing on the bit that’s about the problem, or the bit that’s about the tool? Both are needed.

If Object-Oriented Programming were announced today

Here’s an idea: the current backlash against OOP is actually because people aren’t doing OOP, they’re doing whatever they were doing before OOP. But they’re calling it OOP, because the people who were promoting OOP wanted them to believe that they were already doing OOP.

Why is that? Because the people who were promoting OOP wanted to sell their things. They were doing this in the 1980s to 1990s, when you could still expect developers to spend thousands of dollars on tools and libraries. “Here’s a thing that’s completely unlike what you’re already doing” is not as good a sales pitch as “ride the latest wave without boiling the ocean”. Object-Oriented principles were then hidden by the “Object Technology” companies – the StepStones, NeXTs, OTIs, OMGs – who wanted to make OOP accessible in order to sell their Object Technology.

That’s not the world of 2017. Nowadays, developer environments, libraries, deployment platforms etc are largely open source and free or very cheap, so rather than make an idea seem accessible, people try to make it seem important. We see that with the current wave (third wave? I’m not sure) of functional programming evangelism, where people are definitely trying to show that they are “functionaller than thou” rather than that you already know this stuff. Throw up a github or an npm that uses monad, pointfree or homoiconic without any indication of shame, and you’re functionalling right. Demonstrating that it’s already possible to curry methods in Objective-C is not the right way to functional.

If OOP were introduced into this world, you’d probably have a more dogmatic, “purer” representation of it than OOP as popularly practised today. You might even have OOP in Java.

One thing that makes me think that is that, well, it’s happened and it’s true. Multiple times. As previously explored here, protocol-oriented programming is just polymorphism under another name, and polymorphism will be found in a big-letters heading in any OOP book (in Barbara Liskov’s “Program Development in Java”, it’s chapter 8, “Polymorphic Abstractions”. In Bertrand Meyer’s “Touch of Class”, section 16.2, “Polymorphism”.

Similarly, what are microservices other than independent programs that maintain their own data and the operations on those data, performing those operations in response to receiving messages? What is a router at the front of a microservice but the message dispatch handler, picking a method implementation by examining the content of a selector?

Open Source: because I got mine, so fuck you

The Free Software movement has at its core the idea that people have the freedom to use, study, share, and improve the software on their computers. The modern developer “ecosystem” has co-opted this to create a two-tier society: a developer has the freedom to use, study, share, and improve the tools and libraries that developer puts to use in creating software that must be accepted as-is, and used only in the ways permitted in the Terms of Service and End User Licence Agreement. I’ve got mine, so fuck you.

Your web application is probably split, broadly speaking, into a front-end bit that runs in the browser and a back-end bit that runs on somebody else’s computer.

The back-end bit is on somebody else’s computer, so you chose not to adopt the Affero GPL and don’t need to spill any precious freedom on the consumers of your service. The fact that you’re using Node.js (non-copyleft Free Software), a billion node modules (all most likely non-copyleft Free Software), losing your customer’s data in MongoDB (copyleft Free Software), and deploying on GNU/Linux (copyleft Free Software) with Docker (non-copyleft Free Software) and Kubernetes (non-copyleft Free Software) got you what you wanted quickly and cheaply, but there’s no need to permit anyone else access to those freedoms.

The front-end bit is on your customers’ computers, so you definitely don’t want to accidentally spill any precious freedom there! Even though you used a gallon of polyfills and shims (non-copyleft Free Software) and Angular (non-copyleft Free Software) to get what you wanted quickly and cheaply, there’s no need to permit anyone else access to those freedoms.

And of course you edited all that JavaScript with VSCode (non-copyleft Free Software).

The fact that the browser used to run that JavaScript might itself be Free Software is immaterial. The freedom to not have any freedom is not freedom.

That’s true in the mobile world too. Your free software compilers and runtimes and libraries all go to build opaque blobs that must be run as-is on somebody else’s phone, whether or not that phone has a kernel that’s Free Software. We got the bits to build our platforms and our apps quickly and cheaply, thanks to Free Software. We’ve got ours, fuck you.

But, you argue, this is all immaterial. People who aren’t developers, well, they aren’t developers, they can’t change software, why does any of this matter? Because, as all the crime dramas attest, people need motive, MO, and opportunity. Remove the freedom and the opportunity is removed: maybe someone would go out and learn a bit of programming if they had a problem they wanted to solve and the opportunity to solve it. Or maybe they’d go out and find a gigging coder, or a student who wanted a side project, or one of us pros who needs to keep their activity chart green in order to stay employable. Maybe we would benefit more from our jobs as people who change software, if there were more opportunities to change software.

The current division of software freedom into the haves and the have nots is arbitrary, artificial, and unnecessary.

Technical debt and jury service

We have the idea that in addition to the product development backlogs for our teams, there’s an engineering backlog where technical debt paydown, process/tooling improvements, and other sitewide engineering concerns get recorded. Working on them is done in time that is, by definition, taken away from the product backlogs (because of Sustainable Pace).

A colleague recently described the time spent on the engineering backlog as a “tax”, which is an interesting analogy. A pejorative interpretation is that, like a tax, centralised engineering work is a cost imposed that takes away from realising more value on my direct projects.

A positive spin is that taxes go toward funding the commons: no one of us can afford to build a road between our house and the office, but having roads connecting all the houses to all the offices has strategic benefit to society as a whole (higher productivity, lower unemployment, more opportunities) so if we all pay in a fraction of the cost of a road we can all have a road. Similarly, one product team might grind to a halt if they spend all of their time on the new CD pipeline infrastructure, but all teams will benefit if they all chip in a bit.

This version of the analogy implies that there might be, like the treasury, a central agency deciding how to spend the common wealth. Somebody needs to decide how much tax everyone should pay, what to do with dissenters (is it OK if your product team focuses on its sprint for a fortnight and doesn’t do any of the engineering backlog?), whether to accept overpayments, and what those tax dollars should go on.

Only it’s not tax dollars, it’s tax hours. In this sense, a better analogy is conscription (I originally thought of the Anglo-Saxon fyrd, maybe jury service or non-military national service is a less aggressive way to consider this). Taxation means that I give all of my work time to Wealth Wizards but give a chunk of my money to the government. Conscription means that I don’t get to give all of my time to my employer: some of it has to go to the commons. Maybe Jonathan and Rebecca can’t give any time to their product teams this week because they’ve been “called up” to the engineering backlog effort.

That seems like a useful analogy for these tasks. I can think about what resources are available for products or “the commons”, because I can think about whether someone is working on “the day job” or has been conscripted. Maybe it doesn’t make sense for everybody to have equal likelihood of being “called up”, in the same way that it’s easier for students to get out of jury service than for full-time employees.