Category Archives: Software Development

Standards Help Me Port My Brain, Not My Programs

SQL is a well-established standard and is implemented in a consistent way across a variety of DBMS’s. In my career, I’ve worked with MySql, SQLServer, SQLite, and Oracle. My knowledge of SQL goes over pretty unchanged.

My code for all of these systems is hopelessly incompatible.

I can’t grab SQL from my node app and put it in my iPhone app, because there’s more to it than the SQL. The SQL is embedded in the host language and often abstracted in a library (like TypeORM, Django, CoreData, or JDBC). These languages and libraries are completely incompatible with each other. I tend to use ORMs, but this would be true even without them because you often want some kind of composability with your SQL clauses. You’d have to build that in the host language and then spread partial SQL strings around your code.

Even if you tried to keep your SQL pure and outside the host language, you’d run into portability issues. Each of the individual DBMS vendors add to their implementation of SQL in mutually incompatible ways (mostly for good reason). User needs often outpace the speed of the standardization bodies, and so the vendors need to innovate outside of the standard.

You run into this with web programming because there is no standard way in SQL to grab rows N through M from a SELECT. In order to implement paging, you need to be able to do this efficiently. SQL has also been extended in incompatible ways to store external blobs. Even simple things, like dates, are not consistent.

And, I haven’t even mentioned stored procedures.

But, even though SQL doesn’t help me port my programs, SQL is a good way for me to think about data. I can show it to other developers or DBA’s, and we can have a conversation. Most of my data code will be based around SQL in some way and will be understandable to any developer who knows SQL.

My programs get many benefits from SQL, but not portability. And this is true with standards in general. Instead of Java’s promise to “write once, run anywhere”, we live in a world that React Native calls “learn once, write anywhere”, and it’s not bad.

Co-working First Impressions

I started working remotely in 2013 and haven’t spent significant time in an office since then. When I went to Trello’s NYC office, it was mostly for offsites or to onboard a new team member, so I wasn’t planning on getting a lot of programming done. Even so, the Trello founders were highly influenced by Peopleware, and knew that company offices needed to provide a quiet working environment.

But, now that I’m working alone, I do miss having some interactivity with people during the day, so I am trying out a co-working space once a week. Today is my 2nd day.

Some random thoughts.

I am really glad I brought noise cancelling headphones. It’s just enough (along with listening to ocean sounds) to drown out the one-sided zoom meetings when I am trying to code or write (luckily, it seems to be somewhat rare).

Sitting by a window is nice. It’s on the third floor across the street from a residential neighborhood. All I see are shade trees, rooftops, and the big blue sky. At home, my window is on my left, slightly behind me. This gives me a place to stare to rest my eyes.

A view outside of a co-working office showing tree tops, rooftops, and the sky

The weather is hot enough for shorts and a t-shirt, but like everywhere else in Florida, when you get inside, they have the AC cranked up. I get to wear jeans, which I miss.

I thought I would miss my monitor more. They have a place to store one, and I was already planning to do that, but I’m getting a lot done right now without it. If that keeps up, I probably won’t bother.

Doing this on Mondays sets up the week well. I guarantee that I won’t have a meeting (because I am blocking the whole day). I would not have done that if I was working from home. Since I end each week with a plan for the next week, I can just get going when I get here.

They have regular and counter-height desks. You can stand at the latter.

Audio Copilot

When I first wrote Robotic Pair Programmers I really was imagining that an AI code assistant would be more like a side channel and not inside autocomplete. To be fair, I was programming mostly in Xcode at the time and had not yet experienced VSCode, so my conception of how fast an IDE could be was antiquated.

I do think that the autocomplete integration is genius, but I have also been wanting an audio side-channel to my work that would be more like a human assistant. Think of it as a ChatGPT enabled Siri, but still fully integrated into my IDE.

Here’s a script of what that might look like (NOTE: the first draft of this script was generated by ChatGPT, which I am pointing out because I think it’s interesting in this case. I edited it to fit the article better.)

Programmer: “I want to do a binary search in Python. Can you help?”

AI assistant: “Sure. One way to start is to define a function called binary_search that takes in a sorted list and a target element. Inside the function, you can initialize variables called left and right to the beginning and end of the list, respectively.”

Programmer: “Okay, I’ve defined the function. What’s the next step?”

AI assistant “Next, you can use a while loop to iterate as long as the left index is less than or equal to the right index. Inside the while loop, you can calculate the middle index by taking the average of the left and right indices.”

Programmer: “Got it. And if the element at the middle index is the target element. I can return the index. What do I do if the middle index isn’t the target element?”

AI assistant: “If the target element is less than the element at the middle index, you can set the right index to be the middle index minus one. If the target element is greater than the element at the middle index, you can set the left index to be the middle index plus one. This way, the while loop will continue until the target element is found or the left and right indices cross each other.”

I would expect that the assistant would make incorrect assumptions or mistakes and then the programmer would clarify.

More importantly, when the programmer is programming, the AI assistant will still be making suggestions via autocomplete, but now is much more aware of the goal and so we’d expect the suggestions to be better.

The much bigger win will be when the assistant doesn’t wait for my requests, but interrupts me to help me when I am doing something wrong. To continue the binary_search example, if I set left to the middle index (off by one) then the assistant would let me know my mistake via audio (like a human pair would).

Just like in Assistance Oriented Programming, I think the key is to get intent in Copilot as early as possible.

Addendum

This example is simple, but I generated lots of interesting scripts in ChatGPT where the programmer and assistant collaborated on

  1. Testing the binary search
  2. Doing quicksort together, but I asked ChatGPT to make the assistant make incorrect assumptions that get corrected.
  3. Building a burndown chart in a web based bug tracking program

They were all interesting, but I didn’t include these because the that isn’t the point of the article.

Assistance Oriented Programming

Here’s a simple SQL query:

SELECT p.id, p.name, p.age FROM person AS p

It’s a reasonable syntax for a simple operation. But, when Microsoft designed LINQ, they decided to put the datasource first:

from p in person select new { p.id, p.name, p.age }

LINQ was designed knowing that it would be used in Visual Studio, and so Microsoft made it easy for the IDE to autocomplete. If you tell it the datasource first, it will know the possible fields when you type dot.

The obj.member syntax predates modern IDE autocomplete. Even SQL is using it in the above example. The innovation in LINQ is getting the object name and type into scope before you need to access any fields.

So, as I continue to play with GitHub Copilot, I wonder if its widespread adoption will spur on language features that are designed to make better use of it.

One thing holding back that innovation is that Copilot needs to have a giant corpus of examples. If you make a new language, then Copilot can’t really help you write it. And if the main reason to use it is that it can be more easily assisted, then it can’t get off the ground. One way to get around that is to generate a corpus if the new language is a simple transformation of an existing one.

A more likely scenario is that programming style adapts to be more easily assisted. We see from the LINQ example that we want to get names and types known as quickly as possible. This will help AI assisted programming as well, but even better is getting your intent in the code as early as possible.

Two ways we signal intent in programming is with names and with comments. To get assistance, you should use good names. I notice that the quality of the suggestions is based on the names I use.

I’m torn about what will happen with comments. Comments that describe code aren’t worth keeping, but may result in good suggestions. Hopefully, most programmers won’t leave those comments in.

I wonder how good Copilot can do with comments that are more declarative. And if it’s good at it, will a loose, declarative, pseudocode that is embedded in comments become the de facto Assistance Oriented Programming Language I’m looking for?

Initial Thoughts on AI Assisted Programming

I’m fairly active on StackOverflow, so when people started answering questions by copy-pasting from ChatGPT, I noticed. Unlike the AI generated code shared on social media (which appears to be cherry-picked), in actual use, the ChatGPT answers were nonsense. And so, StackOverflow banned its use.

The problem was not that ChatGPT was generating nonsense — the problem was that people were posting the nonsense without vetting it. That’s partly because the answerers were not even trying (they thought it was fun), but also, I would guess that many of them didn’t know if the answer was good or not. The answers were obvious nonsense to experts, but less so to beginners or laypeople.

Right now, AI generated code can’t be used without an expert. But is it useful if you are an expert?

Two years ago, I said that I wanted a Robotic Pair Programmer. In that post, I made some suggestions for what I’d want:

One way that seems fruitful to me is rare API calls. There will be times when I am using an API that appears very infrequently in the corpus or my own repositories. In that case, it should infer that I probably need more help than usual and offer up a tutorial or the top Stack Overflow questions.

And …

Another trigger might be my new comments. If I comment before I code, then it should be interpreted as a search query:

// Parse the JSON I get back from the data task

That should bring up links to likely API classes in the help pane (just like it would if I already knew the class). Maybe offer up imports to auto-add. Maybe offer a snippet.

One important thing is that this needs to be just in the IDE, not a chat interface. And last March, GitHub Copilot was released as a VSCode plugin. I ignored it back then, but seeing how far ChatGPT had come made me think that Copilot had a chance of being good.

The best thing about Copilot is its UI. It feels like autocomplete, but offers up more than usual autocomplete would. Sometimes it can complete a line — sometimes it can give you a few lines. In either case, since IDE users are already used to this interaction, it doesn’t get in the way. It’s also fast, which is vital.

It also does the comment trigger I wanted — meaning, I can comment my intention it will offer up snippets. Many of these are useful.

One worry I had in 2021 was that a system like this would offer bad suggestions often. And, that’s the main drawback to Copilot. Much of what it suggests is wrong. Some of it is scarily accurate, and the rest is in-between — not right, but still helpful. After a few weeks with it, I can’t decide if this is the right balance. I haven’t felt the need to turn it off, but also, I’m not sure I’d miss it once it was gone.

My main reason for trying it out was that it feels obvious to me that this is the future of programming. I program mostly for myself, so I really want the productivity gain. It costs $100/year, and I think that’s a no-brainer, because it pays for itself pretty quickly in productivity gains. If you use VSCode and one of the languages it supports, I’d recommend trying it out.

Lessons from My First Real Program

(I commemorated this program in a t-shirt)

In 1983, in the wood shop of JHS 125 in Woodside, Queens, perhaps sensing that my friend and I we’re acting up out of boredom, our teacher asked if we’d rather be in “computer shop”. Mr. Abbe had good instincts.

My new shop teacher, Mr. Penner, gave us each a sheet of graph paper and told us to count 80 columns and 25 rows and draw a picture by filling in squares. I drew a skull (I think it might have been getting close to Halloween). Next, he taught us enough BASIC so that we could write a program to draw our pictures on the screen.

I looked up an old Commodore PET manual, PETASCII table, a PET memory map, and downloaded VICE, a PET emulator, to recreate it here (lines 1010+ would have the coordinates from the drawing):

10 REM DRAW AN AWESOME SKULL!!
20 REM BY LOUIS FRANCO
30 READ X, Y
40 IF X = -1 THEN END
50 POKE 32768 + y * 80 + x, 160
60 GOTO 30

1000 REM (THE X, Y COORDINATES FOR THE SKULL)
1010 DATA
1020 DATA
2000 DATA -1, -1

What I love about this introduction to code:

  1. It is pretty much the minimum code you can write to get a drawing on the screen.
  2. It has conditionals and loops, so it’s more interesting than Hello World.
  3. It takes advantage of something a 13 year-old might know — X, Y coordinates.
  4. He gave us lines 30-60 because you have to know minutia of PET architecture to understand it. He did explain it, but didn’t expect us to be able to write it.

The next lesson was to animate the drawing (cycling two frames), which we were mostly on our own to do.

T-shirt showing a PET computer with a pixelated skull on it in green

Extending GraphQL Code Generation (Part II)

In the last post I said that even though I had used code generation throughout my career, the expressiveness of modern languages had reduced my usage. But, I had found that connecting code to external systems was better if I generated a type-safe API layer.

GraphQL Code Generation (which I mentioned in the last post) does this for GraphQL client code. You provide your queries and the endpoint to your server, and it will validate the queries and use the server’s schema to generate type-safe Typescript wrappers to use to execute those queries, including react hooks.

It doesn’t go far enough for me, but since it supports plugins, it was easy to add what I wanted. All I wanted was simpler, non-hook, type-safe functions to make GQL requests. The hook variants are very convenient when you are just populating a screen, but I had times when I wanted to make GQL requests and didn’t want to handle the result in the React lifecycle. This was even more true for mutations.

So, as a first step, I made a non-type-safe function to do any mutation given an Apollo client and document (NOTE: DocumentNode is a type that represents a GQL query).

export const gqlMutate = async (
  apolloClient: ApolloClient<object>,
  mutation: DocumentNode,
  responseProperty: string,
  variables: any,
  onSuccess: (response: SuccessResponse) => void,
  onError: (err: string) => void
): Promise<SuccessResponse> => {
  try {
    const response = await apolloClient.mutate({ mutation, variables });
    // handle response and call onSuccess or onError
  }
}

I could just stop here, but then when I write a call to gqlMutate, I get no code completion or type-checking.

But the GQL code generator actually generated concrete types for all of my GQL queries, so I could write a wrapper, like this:

export async function requestCreateOpening(
  apolloClient: ApolloClient<object>, 
  variables: gql.CreateOpeningMutationVariables, 
  onSuccess: (response: gql.SuccessResponse) => void, 
  onError: (err: string) => void): Promise<gql.SuccessResponse> {
  return await gqlMutate(
    apolloClient, gql.CreateOpeningDocument, 
    "createOpening", 
    variables, 
    onSuccess, 
    onError);
}

Which works great, but I have hundreds of mutations and I would have to write this code over and over and keep it up to date if anything changes. But, literally everything I need to know to write this is in the GQL query document. The code generator already generated this constant and type for me.

export type CreateOpeningMutationVariables = Exact<{
  teamId: Scalars['Int'];
  name: Scalars['String'];
  body: Scalars['String'];
  whatToShow: Scalars['String'];
}>;

export const CreateOpeningDocument = gql`
    mutation CreateOpening($teamId: Int!, $name: String!, $body: String!, $whatToShow: String!) {
  createOpening(
    teamId: $teamId
    name: $name
    body: $body
    whatToShow: $whatToShow
  ) {
    success
    error
  }
}
    `;

It’s not a stretch to get it to generate the wrapper around gqlMutate as well.

Normally, the drawback is having to parse the source language, in this case GQL. But GQL code generator already does that and already generates a lot of useful types for you. All you need to do is loop through your GQL documents (which are types) and write out the code. For details see the plugin documentation.

It’s too hard to explain my generator (write me if you need more help). But, if you are calling GQL with Apollo and typescript from your front-end, consider code generation. And, if you find yourself writing non-type-safe wrappers, know that you can extend it with plugins to make them type-safe as well.

Why I am Using Code Generation Again, Part I

In the nineties, I used Rational Rose and Perl to generate a lot of C++ code. I’m not sure if I knew the term at the time, but we also created DSLs (Domain Specific Languages) that probably fell into the “… poorly specified, half-implemented Lisp” trap.

Back then, I would say that I used code generation to overcome the limited expressiveness of my programming language. There was a lot of boilerplate in C++ (and later Java and C#) that I didn’t want to write—I didn’t even want it to be part of the source. I considered the DSL to be the source in these cases.

When I moved onto iOS development, I used code generation sparingly. It somewhat complicates the build process and the Swift language designers prioritized expressiveness enough to make it less necessary.

But, recently, I’ve begun to use code generation more while using Typescript. Typescript is a lot like Swift, so it’s expressive enough, but the main difference is that I am using Typescript to access things that don’t have types in their built-in API (SQL and GraphQL), but do have an underlying type system.

I decided to use TypeORM and TypeGraphQL, which allow me to define types that map onto the database and are passed around in the the GQL, and so on my backend, I have a strongly typed API to both. These systems are based on “decorators”, to tackle the problem of boilerplate code. But TypeORM generates the SQL to build the schema and each migration as I add to it, so there is code generation as well.

This is great for the backend. My server ends up exposing a GQL API which can also provide a schema. But, I still have the problem of having a typed API to GQL from my front-end. For this, I ended up using GraphQL Code Generator, which can connect to a GraphQL server, read the schema and your GQL queries, and generate a nice typed API, including React hooks for fetching your data.

Even with all of this, I didn’t think of myself as generating code. I was not writing parsers or generators—I was just using a system that happened to generate code. But having this system in my build flow already (and the fact that GQL code generator can have plugins) made it inevitable that I would eventually be generating my own code.

GQL Codegen is already doing all of the heavy lifting of getting the schema and parsing it—you can write a new plugin in a few lines of code. Once I wrote the plugin, I could delete hundreds of lines of boilerplate code around doing GQL mutations without having to resort to typeless wrappers. I’ll provide some details in Part II.

Seeing Code

There’s a saying that code is written to be read by programmers and incidentally to be run by a computer. I would argue that that doesn’t go far enough. The goal shouldn’t be readability, it should be to make it so you don’t have to read it.

When I have been working with a codebase for a while, I don’t need to read the code all of the time. I see it. I’m not perceiving it serially as lines of code—it’s more like seeing it all at once.

Some of this comes from familiarity. It’s in my head already. The code itself is a reminder. It’s something that comes from flow and extended time with the code.

But, it’s mostly a function of the code itself. Some code is easier to perceive this way.

What helps me is if the code is made up of memorable and predictable chunks. In a seeable codebase, I read one file and just know how the others would look without having to read them. When I open another one, it looks how I pictured it.

This idea is related to System Metaphor from XP

The system metaphor is a story that everyone – customers, programmers, and managers – can tell about how the system works. It’s a naming concept for classes and methods that should make it easy for a team member to guess the functionality of a particular class/method, from its name only.

and Convention over Configuration from Ruby on Rails.

The same goes even when you understand how all the pieces go together. When there’s an obvious next step for every change, we can scoot through the many parts of an application that is the same or very similar to all the other applications that went before it. A place for everything and everything in its place. Constraints liberate even the most able minds.

and the Principle of Least Astonishment

The principle aims to leverage the existing knowledge of users to minimize the learning curve […]. In more abstract settings like an API, the expectation that function or method names intuitively match their behavior is another example. This practice also involves the application of sensible defaults.

Astonishment is perhaps the best way to perceive code that resists being seen. It may be perfectly readable and understandable, but it should also already be predictable. If it surprises you, then you should ask yourself why.

The Unreasonable Effectiveness of Mathematics in Programming

(with apologies to Eugene Wigner)

My college didn’t have a CS major, but they let me put together one under a general engineering program. To fill up the requirements, I had to take a lot of math, which has been more useful than I expected a programmer.

I didn’t seek out jobs that required a lot of math. I optimized my job search around small companies doing product development for B2B, and didn’t care much about the specific technology they used. But, I was comfortable with the math, so it made my life easier.

The first eight years of my career was in FinTech, and the software I wrote was a nice UI around a lot of math. The core concept was options pricing (probability and statistics) and the sensitivity of that price to its inputs (calculus and differential equations). To do risk analysis, you have to build up huge matrices (linear algebra) for various purposes. Our company employed mathematicians, so we didn’t do the research, but we had to understand it to work on those parts.

Later, I contributed to a patent related to spreadsheets where graph theory was important. I also implemented numerical differentiation and root-finding algorithms for it as a way to run expressions backwards (numerical analysis and calculus). That patent expired, so I am reimplementing it in Swift and Typescript.

In 2005, I did a consulting project to implement a distributed monte-carlo engine for a decision support system. I would not have won this bid if I did not understand the math behind the engine.

From 2006-2013, I worked at an image processing tools vendor. This job was the closest to pure CS that I have had, and there was a lot of math, specifically linear algebra, but also some numerical analysis.

Every front-end position I have had uses at least a little linear algebra (for affine transformation). It’s not like you are doing the matrix multiplication yourself, but you’ll understand the more complex compositions better if you understand them. For example, if you know that matrix multiplication is not communicative, you’ll get why the order of the transformations matters.

Nearly every programming job now requires you to understand the analytics data that the software generates and to do statistical analysis on it. Forming a hypothesis and getting evidence to support or reject it is essential. At a bigger company, you might have a data-science team to do the heavy lifting, but it really helps if you can do it yourself—you also want to be able to read and understand their reports.

If you really want to go deep into the type theory behind type-safe languages (like Swift or Typescript), you have to understand set theory and maybe even HoTT. You don’t need it to use these languages, but if you had interest in compiler theory or implementing a language like this, it would help. Understanding set theory also helps with understanding relational databases.

When I was trying to find a Swift implementation of numpy a few years ago, I ended up finding Surge and contributed Eigen decomposition to it. I had to relearn it, but I would not have even tried if I hadn’t touched linear algebra since college.

Games are essentially physics simulators, which are ultimately math engines. I only write simple games as a hobby, but even for pong, I had to write vector math functions.

And, although I think my career has used a somewhat typical amount of math, there are programming jobs that require a lot of math. A deep neural network’s guts are a calculus and linear algebra engine (see this video from 3blue1brown). As I mentioned, data science makes heavy use of probability and statistics. I learned in Quantum Country that the “programming language” of a quantum computer is based on matrix multiplication with complex number entries. And while writing a game is a reasonable amount of math, writing a game engine is much more than that—and as more things (the blockchain, games, machine learning) have used the GPU for computing, the way you think of implementing solutions in those domains is more mathematical.

To be clear, you can do a lot of programming without understanding any of the math behind it. But, I have found it more enjoyable to go a little deeper, and it makes it easier to develop intuition about the higher level concepts.