Future of Coding - On The Maintenance Of Large Software: James Koppel
Episode Date: September 22, 2018How do we maintain millions of lines of code? For example, the Social Security Administration has 60-million-lines of COBOL. James Koppel is building tools to help tame these kinds of beasts. His curr...ent work is on decreasing the costs to build developer tools by allowing the same tool to work on a variety of languages. James Koppel is a Carnegie Mellon CS grad, Thiel Fellow, entrepreneur, educator, and currently PhD student at MIT. We talk about his experience withprogram repair, program sythesis, code comprehension, and many other cutting-edge fields relevant to the future of software engineering. Transcript and episode notes: futureofcoding.org/episodes/30Support us on Patreon: https://www.patreon.com/futureofcodingSee omnystudio.com/listener for privacy information.
Transcript
Discussion (0)
Hello, and welcome to the Future of Coding. Today, I have James Koppel on the podcast.
James is a Carnegie Mellon grad. He's also a Thiel Fellow. I think part of his fellowship,
he started Tarski Technologies, where they did program repair, which we're going to hear about
in a second. He's also worked at a company called Semantic Designs, also in this field of tools for
programming languages, which we'll hear about soon. And he's currently doing a PhD in programming tools at MIT and runs a coaching business helping programmers become better, like more advanced. So welcome,
James. Hello, Steve. Glad to be here. Yeah, really excited to have you here. I think there's a lot of
really interesting topics that you are an expert in that kind of surprisingly to me,
I don't know very much about so i i assume
many of our listeners might also know no let know not very much about i think what i'm trying to say
is um there are a lot of topics in this field that are kind of trodden ground uh and and there's
and there are you're familiar with topics that aren't part of that. Yep. Yes.
I always like to joke about some of the fads, like deep learning.
No one likes it because it's too popular.
And it's definitely me.
I'm a counterculture at heart.
I like things because they're obscure, because I think they're important, but underappreciated.
And I'll be happy to share a lot of that today.
Cool.
Well, so I thought because reading some of your writings, I got the sense that you are someone who has been in this field for a little while
and you've gone down a lot of paths that proved to be dead ends
and then backtracked and then gone down different paths.
I thought it would be interesting to follow your story in that kind of more narrative way so
we can try and learn some of the lessons you learn the hard way but you know a little bit easier
because we just can listen to it as opposed to living it like you did so i thought we'd start
at the beginning um who who are your who or what were your first influences that made you excited
about working on improving programming at like the tooling level or at the language level?
So probably the best thing that ever happened to me
is I don't know what it was,
just some combination of having seen the words,
this project will be rewritten one too many times.
One day in May 2010, I just woke up,
and it hit me that the world does not know how to write good software.
And ever since then, I've been very passionate about programming tools.
And I happen to be in a very good place to suddenly get that passion.
Carnegie Mellon is the world's number one university in programming languages and software engineering.
It's one of the relatively few top schools to have a very strong research group in software engineering.
So a huge influence in my early development was Jonathan Aldrich,
who was my undergraduate research advisor at CMU.
What kind of work do you guys do together? And what kind of work is he known for?
Funny enough, he is one of the very few people in programming languages research
who actually designs programming languages.
He has a pretty famous feud
with another type theorist, Bob Harper,
about whether it makes a difference to a language.
Bob Harper would say that SML
and Haskell both had monads. There's no difference. Jonathan Aldrich says it's actually very
important to the language design that monads is in the Haskell standard library and has
special support for it, but not in languages like OCaml or SML.
So the big thing that we worked on together was the Plaid language or type state oriented
programming.
Type states become a little bit more well known in recent years because of Rust.
Things like the Baro checker and Rust are formed to type state.
So type state is all about the idea of...
So types let you prevent a program from passing in bad values.
It's like basic things like you want a string, can't pass an integer. Also more advanced things like you need a positive integer or you need a valid person.
Type state lets you put protocols into the type stem.
And they found in a empirical study, I think the number was 13% of objects have some kind
of protocol.
That means that methods must be called in a certain order.
The canonical example is for files, you must open them before they're read.
And in Plaid, you'll actually have separate types of file for open file and not.
And a closed file does not have a read method.
But when you open a file, it suddenly transitions into an open file. He got a lot of press a few years ago
for the Wyvern language. Its big thing is TSLs. You've heard of DSLs, the main specific
languages. These are TSLs, type-specific languages. And the idea is that, so I have a function which takes in a regular expression or some
HTML, and so its type is, say, takes in a regular expression.
Well, the compiler sees that, and now it switches to a regular expression parser or an HTML
parser.
And so you can mix many different languages, many different syntaxes into the same file in a way which is a lot more principled and elegant than other solutions.
It was funded by the NSA because the NSA does random security things, which somehow that part of the headline got a lot of news.
The Monad feud. That sounds fascinating, but I didn't quite catch the disagreement there.
At CMU especially, there are some very hardcore theoretical guys who believe that the core
semantics of a language are all that matters.
The core types stem. all that matters, the core type's stum. And things like, so if I add a feature to a programming
language but that feature is very difficult to use, they would say that programming language
has that feature, and that's what matters. And Jonathan actually thinks it's important
what people actually do with the language.
Got it. Okay. And then these TSLs, I didn't quite catch the difference between
them and DSLs.
It basically is a DSL, but so you call a function, this function expects an
argument of a certain type, and based on that type, you can just pass in a parameter,
declare a parameter with a DSL, and the compiler knows which DSL to use based on the type.
Oh, okay.
I got it.
That makes more sense.
So, okay.
I got it.
That makes a little bit more sense to me.
Okay, cool. There was a quote that you had sent me that the claim was that software
maintenance is the number one solvable impediment to social progress. Yes. Did you believe that
early on or was that something you came to realize over time? Not exactly when I started to believe that. And back to that a bit,
obviously saying the largest is a very hard claim to make because you have to go through every other
possible candidate to get compared to a quantifier. I will very much defend the claim that it is a
very, very, very large impediment to societal progress and that all kinds of change are at
some point limited by our ability to change software.
And this change is far harder than is needed.
And there also are very purely technical solutions
to the problem.
Technical solutions that can reduce the cost
of software maintenance by a factor of 100
in a relatively near future timeframe.
So over the course of a couple of decades.
Got it.
Okay, that makes a lot of sense.
I think I want to harp on the word you use,
software maintenance.
It seems like a word you use a lot.
In my mind, software,
maybe I guess I'm kind of getting at
how broad is maintenance.
Like if you have a program that lives for a long time and you're constantly adding to it, is that...
Because to me, maintenance is more like just making sure that it continues to run.
But is incrementally adding features also maintenance?
Yes, it is.
Basically, anything that someone with a job title maintenance program would do is maintenance.
So not like an application developer,
like if I'm a regular software engineer.
I mean, any, any developer does maintenance.
So a lot of the examples I have in mind are like any,
any policy change.
So the social security administration has,
I think it's about 60 million lines of COBOL.
The joke is that it's one line of COBOL per retiree.
Their application talks to 500 separate databases.
So anytime when you want to pass a law that changes Social Security, you throw that over the wall to the SSA, and people there are
going to have to modify the 60 million line COBOL program.
So that's just one government agency of many.
No story there, Cosmetic Design did some work for them on doing programming comprehension
tools.
Basically, any change you want to make to our institutions, if that institution runs
on software, as does the
SSA, then you have potentially a very, very large software maintenance problem that makes the change
much harder to roll out. Yeah, that makes sense. I think when I hear maintenance, I think of a much
more narrow definition of maintaining the status quo as opposed to the more dynamic definition
you're talking about. But anyways, I agree with you. I feel like it's not talked about enough.
I think people focus more on beginning new projects, which to me is crazy because that
happens once. And software maintenance is every other kind of programming,
and it's like the most important kinds.
So anyways, I'm with you.
It was a fairly recent development where I realized,
like, oh, wait a second.
Like, it's big projects. You know, hundreds of thousands, tens of thousands
or hundreds of thousands of lines of code
are the ones that we should be thinking about.
Yep.
They don't get all the flashy announcements announcements but that's where most of the labor
is spent yeah um okay so um so yeah continue uh chronologically so it sounds like the first your
first real adventure uh post uh university was with your company tarski. Yeah. And you guys did program repair.
So maybe talk a bit about what that is and what you tried to do.
Sure.
So the program repair is exactly what it sounds like.
It's a problem of automatically fixing bugs in other programs.
And the general way that this looks is you start with your original program,
which has a bug.
The bug is exhibited by some failing test case.
And you perform some kind of search for a modification to that program in order to make the test pass.
Got it.
So the genesis of this was really when I started the TL Fellowship, I was looking for things to do,
preferably things which would let me keep working in this area once the Teal Fellowship money ran
out.
And I was giving a talk at the Singularity Summit, basically trying to argue that advanced
tools that can accelerate the pace of programming are possible and coming slash year.
So I came across the program repair work and in late 2012, this is starting to become a hot topic
because in ICSE that year, ICSE is the International Conference on Software Engineering,
the largest academic software engineering research conference. Claire Leques at University of Virginia,
she's now a professor at Carnegie Mellon, and her co-authors.
They had published a paper called
A Systematic Study of Program Repair,
Fixing 55 out of 108 Bugs for $8 each.
So I found this paper as I was doing research for my talk,
and I was pretty blown away by the results.
This sounded like something that could be pretty immediately commercialized. So I started working on this probably a little bit or a lot too fast. Basically,
I didn't know all the things you have to check to make sure startup idea is a good idea.
People talk about, oh, the idea doesn't matter, it's the execution. And that is totally false.
Because, oh, ideas are a dime a dozen.
And product ideas are a dime a dozen.
But ideas with the potential to germinate into a business with a sales plan, with a
marketing plan, with a growth plan, which are defensible.
Things that can be done now, things where you can create a cracky prototype and still
sell it and not have to have something perfect in order to work.
Those ideas are very rare.
So I did not know the kind of rigorous testing of the idea that need to be done before committing
to it.
There are also some specific issues to trying to build a company based off research, especially research
which is not your own, which is first understanding that academics are usually a little bit pompous.
Part of their job is to craft a really nice story in a presentation and to funding agencies about how the work is going to have a huge impact. And often the story is based on a little bit idealized. There's a particular
controversy with the GenProg program repair work I was working on, which I can talk about in a
minute. And there's also the fact you can never turn a paper into a company.
You can turn a research area into a company, but not a paper.
Even if you read and understand every paper in an area, that does not make you an expert
in the area.
It does not mean that you'll be able to instantly respond to different variations of the problem
or modify the work to some existing barriers, know what issues can come up.
So much of that stuff is hidden.
And it's not really a good way to get it other than hands-on experience.
So I was basically in the position of learning three very hard things the hard way all at
once, which is business fundamentals, general
software engineering stuff, like how to build a distributed cloud of different program repair
servers, because this is a very computationally expensive approach.
You're running hundreds of thousands of variants of an industrial program, as well as learning
the research side.
So that was a bit too much at once.
I believe it.
So, yeah, you said you were going to talk a bit more about the GenProg approach.
Yeah.
So GenProg, this is, so program repair has become a pretty hot topic nowadays.
There are dozens of papers published in conferences every year about it. So, permanent repair has become a pretty hot topic nowadays.
There are dozens of papers published in conferences every year about it, probably in the order
of 10 or 20 research groups working on this.
There is now an entire website on permanent-repair.org, which collects all these.
I actually used to own that domain because I bought it during my Tarski days, but I sold
it to someone who manages it now.
So the field has advanced a lot, but I was kind of doing this in the early days.
This time, GenProg was the only game in town.
And the way it works is almost too simple,
which is they have a pretty restricted space of patches.
So they're trying to create a patch script.
A patch script has sequences of three kinds of commands,
which are copy one line of code from one place to another
to delete a line of code.
And see, where are they? I think it's insert, replace, and delete.
Some variation of that.
It's been a while.
So later.
It doesn't invent new lines of code from scratch.
Yeah, yes.
It only copies things from one place to another.
Because in their particular approach, if they were to try to invent new lines of code,
they did not have a very good way to prune the search space. It would be way too hard.
Yeah. way to prune the search space. It would be way too hard.
And so something that I discovered, which... Oh. So as a side note, which has actually
become pretty important to the research I do now, most program transformations tend
not to work on the original source code. They usually pre-process it into a simplified version first.
And because of that, it's actually very hard to render the changes back to the source tree.
So for the original system, it was actually pretty hard to understand what it was doing
because you get a result on pre-process code.
It's not obvious.
Like, it entered a line of this preprocess code.
That line corresponds to part of a line of the original code. It can be hard to understand
what exactly happened. So, even though I had all the results and saw all the changes that it made,
it took quite a while to realize that the patches it was producing were actually quite bad.
And a later researcher, who's actually somewhat at MIT,
discovered that the main way it worked,
that most of its patches actually
had the effect of just deleting code until a test passed,
which is still potentially useful.
I sometimes do debugging just by commenting
out half my code, commenting out half of that, but much less useful, especially for something
that's this sophisticated and this expensive computationally.
Got it. That makes a lot of sense.
And so by the time I discovered that, I'd already quite invested, and I did not have it.
And basically I was stuck. It's like, okay, I need to do a different kind of prepared algorithm.
And there weren't very many published at the time for me to draw from.
So at the very end of the days, I tried switching to a different algorithm based on a paper called PAR, which used a small number of patch templates.
Of course, that work had its own problems, which is that it was later pointed out by
another guy, Martin Manbrus, now in Sweden, that most of that system...
So their system reported, oh, we can do all these patches, but actually mostly it just fixed no pointer errors.
But that was also not obvious from reading their paper.
So it's a reason, especially in programming languages
where every paper is reporting on a 10,000 line system,
reading the paper does not give you a very strong understanding
of what actually happened.
Yeah, that does seem like quite a problem.
And now I can see what you mean that you have to be,
that it's hard to implement someone else's paper in a company setting.
Yeah, basically, you should be an expert in the field beforehand,
or you should be in an R&D department where you have time and funding to become an expert first.
That makes sense.
I'm trying to think, I actually have examples that violate this idea.
Or like, just trying to think of companies that came from academia.
So I'll give you one that might not be on your radar.
You've heard of Agitar Software?
Adipar?
Agitar.
They have an A-G-I-T-A-R.
No, I haven't. Agitar was a bit of a big name in the early 2000s.
It was founded by a couple people who had previously founded another successful company.
Things started with a V, but they already had done a major exit.
Two of the founders were Ruben Dume and Alberto Savoia. I've talked about
them. They might have had a third co-founder. I believe Rubenko does have a PhD, or at least one of them does.
But their idea was based on a very famous work by Michael Ernst, who is now at the University of Washington, called Daikon.
Daikon is dynamic generation of programming variants.
And it's an idea which kind of sounds simple, and I've heard random people suggesting similar,
but it's still surprising that it worked this well, enough that Michael Ernst's advisor told him that it was never going to work.
And then 10 years later, he won the best, most impactful 10-year paper award for it.
So the idea is basically, if this function runs a lot, and every single time it's run X greater than zero, maybe X
greater than zero is an invariant.
And you can then present this to a user and say, is X greater than zero an invariant to
this function?
They say yes.
Cool.
Now you have that.
You can use it in verification.
You can also generate tests from it.
So generate a test at X-egraded in zero.
That's essentially what the agitator did.
It would do various kinds of random fuzz testing on your program, present you a list of invariants,
and it would generate tests from it.
Hmm.
That's how it worked.
Yeah. So my impression is that they basically had been just directly inspired by that paper
without other, when maybe it was a lot of general technical experience, but not exact expertise.
I'm also told that there are two other companies that people tried to found based on this Daikon idea,
but the other two were much less successful.
Agitar, rating the tens of millions,
I think they hit about 25 million in the Anorak Avenue.
And then the CEO said,
we have 25 million revenue,
the market's too small,
and they shut down the company.
And that's another interesting story.
That's kind of wacky.
I guess that might be a, I guess maybe their revenue wasn't sustainable.
So when I talked to Ringo, the summary he gave was, just said,
let's see, getting there was very hard.
But so some things happened.
One thing is that when they started the company,
I think they started in the 90s or at least around 2000,
it was pretty common that companies would pay thousands of dollars for an IDE seat.
Then IBM open sourced Eclipse,
and the tools market in general created a bit.
Today we have the culture that people are always chasing freemium models or trying to indirectly
make money off tools because there's now a culture where everyone expects tools to be free.
This is another of my big beliefs right now that that we're in a feedback loop, where people don't want to pay for software tools because software tools worth paying for aren't really out there.
And people aren't making software tools worth paying for a while is trying to convince a bunch of open
source tool developers to refuse to give away their stuff for free in hopes of breaking
this feedback cycle and getting a more functioning tools industry.
That's something that I'm not sure will work.
Yeah, that's tricky.
I guess a lot of us have been thinking about this question of sustainability in open source. Have you seen Nadia Engelwald's work?
Nadia who?
No, I don't know how to pronounce her last name. I think it begins with an E. I think on some places she goes by Nafia. She did a lot of work on the sustainability of open source
and creative models to fund things.
Okay, I'll check that out.
Anyway, what I was going to say to your plan is,
I agree with you.
It's a tough, it's a hard sell.
Price is like a function of supply and demand. And the problem with software tools, from my eyes, is that the supply is
so high, like it's infinite, that the price will always be zero. It's similar to how everyone
wants to be an actor. So the supply of individual
already developed software tools
is infinite.
But for most software tools
that we can build,
it would be very valuable.
The supply is zero.
This is one of the big driving ideas behind the business model of semantic designs,
which is they've spent 20 years building their DMS infrastructure,
making these tools easier to build.
What does DMS stand for?
DMS stands for design maintenance systems. But basically, think of the standard tools that you use to build a compiler, like
the parser generator, pretty printer generator, static analysis frameworks, all that stuff.
And just think that they spent, some crazy guys in Texas spent 20 years building this
very fancy, very integrated infrastructure
that no one else understands. So they can do some pretty crazy stuff in a short period
of time. But there's only been a couple occasions when they've been able to sell the same tool
twice. Usually someone comes and says, I have IBM COBOL 8.
I want you to translate it to Java.
And they say, cool, we did that.
We built a tool for that for another company.
And then they discover it's a different IBM COBOL 8.
And they'd have to build a new tool just for them.
And they say, sure, we can do it.
Price is a million dollars.
And usually people walk away at that point.
So most tools that would be really amazing are just not getting built.
So supply is zero.
That's interesting.
It's interesting because the demand is so high
and nobody's stepping up to solve it.
Yeah, but that's basically the major motivator
of my current research,
which is the market is so heterogeneous
that amazing things for
every problem and every language can be built, and none of them are worth building.
So my research nowadays is all about how to make programming tools more general and easier
to build.
Got it.
Okay.
That helps me contextualize your current research.
So, but before we go on your current research,
do you want to linger a bit on semantic designs and share a few more stories or lessons learned
from working on them? They sound fascinating. Yeah. Semantic designs is just
completely awesome and not many people know about them.
How did you find out? I actually found it a bit randomly in 2012.
I Googled the word re-engineering, and they're one of the first results.
And I followed them for a while, and I had a three-hour lunch with Ira in 2014 when I was visiting New Jersey, Texas for grad school.
And he invited me to come work for them, and so I spent the summer of 2016 working with them.
And basically, I spent those three months trying to download Ira Batch's brain.
Because he's been building programming companies since about 1970.
He's been doing programming tools since for over 20 years.
Actually, really back to grad school over 30 years ago.
So, he has
a huge number of more stories that I've
tried to download in my brain.
Yeah, so where do we start?
What can you share that you think
would be interesting to
maybe
one way to filter out the stories
that you should share is like what has informed
your own direction the most like what what's what store what things did you learn from him that like
you were like oh i thought this was a good idea before but now ira has explained to me that it's
a bad idea um so a big thing is his stories and implementing front ends for different burman
languages semantic designs has over 100 language front ends a language frontmese languages. Semantic Designs has over 100 language
front ends. A language front end, it's not like a GUI. It's a language front end refers to the
parser, the pretty printer, basically anything that deals with the raw input of a language,
which is usually the source text, and converts into artifacts that the other compiler infrastructure can deal with.
So, actually, here is my own story from my time there.
I got the idea going in that Lua was the simplest real programming language.
So, as I was learning their stuff, I decided to build a front end for Lua for them.
And actually, that is not the case at all. Loo is super dynamic. And its lexer
is non-context, is non-regular. It's actually non-context free. Just determining where a
comment starts and ends requires the ability to match the number of consecutive equal signs in their syntax.
So you told me a lot of other stories about other languages which are crazy.
So just the problem of raw decoding infid dreams is actually kind of tricky.
So in Python, one of the first two lines must declare the character encoding of the
file.
So, somehow, you have to guess the character encoding long enough to read the string that
tells you the character encoding in order to read the rest of the file.
They did a project with a Japanese multinational company.
And part of the reason they got the job is that they dealt with
the Japanese version coding, which is called ShiftJIS. And the people were just so impressed,
they dealt with ShiftJIS. That's part of why they got the job.
I think there's some case in Ruby where...
So they have their lexer.
The lexer is the thing that turns the raw source text into a stream of tokens.
So it goes from here's an I, here's an F, to here's an if token.
They have different lexer modes, which is a pretty good idea that other lexer generators
I've seen do not have, where it can see a character and say, all right, we're switching
to a different lexer now.
And they have a stack of these modes.
And they found some cases in Ruby where they actually needed to dispatch on the current
lexer stack. So people will talk about lexing being a solved
problem, but dealing with all the the 80s sequences of real languages is absolutely
crazy. You have languages where keywords, there are things that are keywords
in some contexts but variable names in others. You have some languages where
whitespace,
in every language calls itself whitespace insensitive,
but there are languages that are more whitespace insensitive.
You can put whitespace in the middle of a keyword and it's nothing, or having a space in a variable name.
All this makes me want a checklist.
I guess you were from Aira.
Aira's list for things to not include in the
programming language to make it bad. Because it feels like... Or maybe it's easier than that.
How can programming language designers not make these mistakes? How can they make a syntax that's
easy to parse? Yeah. That's a pretty good question. And that there are a lot of these
things which anyone in program analysis would be super obvious that makes things difficult. So, one example is in the IEC 6113 state standard, which is this international standard for certain
kinds of control systems.
There is one part of it, I think it's a lateral logic language, but there are five different
languages in the standard, so don't quote me on which one it is.
There's one part of it which is basically a long change of if conditions and actions.
If this controller is a signal greater than 80, then do this.
Very long change of this.
So that could be paralyzed.
You can try all these conditions at once. Then someone decided to add goto
to this language. If you have a goto, then all of a sudden there are dependencies in
the control flow between things that used to be control independent. And therefore they
cannot be paralyzed. They must have some kind of sequential order.
Got it. So to me, it sounds like we're talking on two different levels.
So the go-to is like on the program verification, like semantically.
And so all these things kind of fit together as far as decisions you can make in the language design,
which makes it harder to analyze, harder to parse, harder to lex.
Well, I think harder to analyze is like a higher level than parsing and lexing.
Okay, so just talking about lexing and parsing,
a simple, there's an easy thing that if your own parser and lexer
in your compiler infrastructure is easy to write, then so will others.
And if you restrict yourself to something that could be parsed using Antler or using some kind of CFG generator, then other people can do so too. If you don't rely on parse actions,
so if you can parse your program and then interpret it,
it has something that runs in the middle and says,
if you see the symbol, do this,
and that affects whether something later is valid.
So if you don't add any context-sensitive kind of stuff,
then it'll be easier to deal with.
Got it.
Okay, that makes a lot of sense.
I feel like if you,
it's almost like the answer is as simple as like,
make sure it's a context-free grammar.
Yes.
I get you like 80% of the way there.
Yeah.
Got it.
But I do, I think,
I haven't really thought about that problem too much.
My thoughts tend to linger more on where you were going with like,
how do you make programming language that don't have bad constructs
that decrease your power of casual inference?
Yeah.
And so GoTo is the classic example, GoTo considered harmful.
But I was kind of going through a lot of the things in programming languages
and recently, you know, in my mind,
and it occurs to me that almost everything is harmful.
Or like there may be more harmful than,
there may be more things to get rid of than things to remain.
And it depends what your goal is.
There's a very classic tension between flexibility
and the ability to reason. And that's the more things that can happen in one place, then the more things you can do, the more things you have to think about.
So an example of this is inheritance.
Inheritance says I can have one function which can be overridden to mean many different things,
and there's dynamic dispatch that determines which one.
So that lets you do a lot of things,
and people have done some pretty hardcore stuff with this.
So, for example, you can take a language like Ruby or JavaScript,
or Julia, and you can override all the basic
functions like plus and times in order to return a parse tree instead of actually doing
the operation.
And you can implement all kinds of advanced tools based on that idea, like symbolic execution.
But that also means when you see one x plus one, you don't actually have any
idea what's happening. And so there's this very beautiful line from a classic essay by
William Cook called On Understanding Data Abstraction Revisited, which I think is one
of the best readings of computer science I've ever seen.
Wow, I haven't heard of this one. Can you repeat it one more time? On understanding?
On understanding data abstraction, revisited by William Cook. He's a professor at UT Austin.
Cool. I'm excited about this.
There's also the follow-up, which written by my advisor at CMU, Jonathan Aldrich,
which I have old teeny and not acknowledge acknowledgement on, which is called The Power of Interoperability,
Why Objects Are Inevitable.
Both of these are very good
because most industrial programmers
and most academic research as well
have a major misunderstanding
of what objects in object-to-orchid programming
actually are.
And theorists have come up with very good answers to this question, which explain very
well when objects are the right thing to use, when they're not, when inheritance is a good
idea, when it's not.
But most people don't know this stuff. The reason I brought this up was a little side comment
in the first essay I mentioned,
which is objects have been written designed to be as flexible as possible.
It's almost as if they were designed to be as difficult to verify as possible.
Yeah, I'm with you.
And I think we all feel that tension.
You can describe it in a lot of different ways.
Like people who like interpreted languages, dynamic languages,
versus people who like static languages, compiled languages.
It's like, yeah, you have Ruby and JavaScript and Python on one side
and Haskell and Rust on the other side.
Yeah.
And I want to point out that the generalized version of this tension
happens not just in language design, but really every single time you call a function.
And that's the caller has a desire for the preconditions of this function to be as broad as possible so that it can apply to the current situation.
Whereas the function has a desire to make the preconditions as narrow as possible.
So the function is easy to write.
That's interesting, because I think when I'm using a function, when I'm the caller as a programmer, I want the function to...
I'm so nervous, I have no idea what it's going to do. I kind of want it to be as narrow as possible.
So if the
precondition is so narrow that it does not include
the current state,
then you call
it without satisfying the preconditions
and its contract says anything
can happen.
So you want it to be easy
to satisfy the preconditions.
Otherwise, I can't even use the function?
Yeah. But conversely, the preconditions are too broad, then it becomes very difficult
to change the function because it's used in so many different ways.
Interesting. Yeah, I think I kind of, maybe'm like very much on the um i like the reasonability
is important to me more than than the flexibility so i i feel like it runs in the opposite direction
for me when i'm writing a function i i want like sometimes i'll get uh you know tricked into making
it more generalizable than than it needs to be.
But when I'm using a function, I really desperately want it to be as dumb simple as possible so I can understand what it's doing.
I'd rather just write my own function than use someone else's function that I don't understand.
I think those aren't necessarily intention.
I'm also not entirely sure I understand your position right
now. Okay. Well, we can move on. Well, actually, I want to linger on this divide a little bit more
because I think it is fascinating. Because it's a little bit dispiriting, I think, for both sides
to think that there isn't a right answer, it just kind of depends on your situation?
Well, any time when two components in software interact,
it's a negotiation.
And, yeah, there is kind of something fundamental about those sides of negotiation wanting different things.
Well, so the perspective of large software projects that we
need to maintain, to me it
seems like
it
seems like in that context, which is most context,
large software projects we need to maintain,
it seems like reasonability and
reasonability
and strictness is so much more important than
flexibility. Because flexibility
makes it easier for you to write things,
but makes it so much harder for you to read things.
And so for me, it's pretty clear.
Okay, serious.
This audio cut out briefly.
What makes it easier to write things in order to read things?
Oh, so having more flexibility on any line of code, anything can happen.
That makes it easier to write things, but it makes it so much harder to read code, even that you've written, but especially code that other people have written.
Okay, so...
So I imagine for you, who you're interested in the maintainability of large programs, you would be much more
on the verifiability.
I'm generally a fan of having pretty restricted interfaces.
A line I like to repeat a lot is that the power of a software design is not what it
can do, but what it can't do.
Your goal as a software designer is to design it in a way where all the things
that you need to happen can happen and all the things that you don't want to happen can't
happen.
And generally, when a function accepts a broader space of inputs, then that goal becomes harder.
And there's a bit of a ratchet effect. You have a function with 10,000 callers that call it
in 20 different ways. You must continue supporting all those 20 different ways of calling it
unless you go around through and change those 10,000 calls. Obviously, there's a continuum of this ratchet effect from a function which is called once in the same file all the way up to the HTTP standard where someone was spelled the word refer back in the 80s and that's never ever going to be changed.
I'm with you.
Okay, there's one other question that's semi-related to this sort of stuff that I wanted to ask.
Then we can move on to Hubix.
So it's very specific to me, but I think it might be common to other people too.
So I thought you might have some good thoughts here.
So I was given a job where I was given a a few thousand lines of
javascript uh that were written years ago and written by someone who was new to programming
and so they're like unmaintainable yep call that cal etc and so they wanted the code to do exactly
what it does now uh but just be cleaner and so it and i just had to do it with with my eyes basically i added tests
but um it just drove me it drove me a little bit crazy that i didn't have like formal tools
to prove that like this on this these code never happened and you know it's more complicated than
like their after return statement like you know they're quite there's some reasoning that has to be done in order to prove that they
don't happen, but it's very straightforward proof.
I have to do them in my head.
There's no formal tools for these kind of things.
I was wondering if you had thoughts on...
Yeah.
And that's going back to what I said earlier, that most tools can be built, just the supply
is zero.
That's most... all these tool things
that we know how to build that could help you with that,
there aren't very many actually around.
When I was at Semantic Designs,
they have an employee who used to have another company
called Blacksbox IT, and they built,
they built some program comprehension tools for COBOL.
One of the things they did was this thing called program slicing.
Program slicing is an idea invented by Dr. Weiser or Dr. Weiss in the 80s where you take
a program, you say, pick one variable or one output and say, give me just the parts of
this program that affect that one output and say, give me just the parts of this program that affect that one output.
And it will trace back and find that for you. So I was surprised to find that in the COBOL world, there actually are a bunch of tools
that do this.
Whereas in the rest of the industry, I had never heard of anything actually used commercially
that does this.
There are lots of academic tools.
It's a very old idea, very well known.
Nothing that anyone could just download and use.
So I'm actually reminded of a JavaScript task that I had to do during my time at Aptimize,
which also involved refactoring some thousands of lines of unmaintainable JavaScript.
And a tool that I wish I had at the time is actually a semantic designs tool, one of their
few off-the-shelf tools, not these custom migrate your tires and things, but a much
smaller thing, where they have one of the world's best clone detectors, meaning that Meaning that it can search through a code base and discover common patterns in the code.
There are a couple other tools that do this.
There's a company called Pattern Insight, I think it was acquired by VMware, which can
also do this at a much greater scale, so like for millions of lines of code,
but it's not as flexible in their pattern matching.
So I looked at the outputs of the Hispanic Designs clone detector,
and I find some very interesting things.
So for discovering common concepts that could refactor,
it would have been invaluable.
Got it. Okay.
You reminded me of one other topic I want to touch on before we move on to the cubics. So there's this whole field, I guess, of program comprehensibility.
So I'm fascinated by this field.
So one approach is program slicing.
What other approaches are there?
I'm not super familiar with this subfield.
There is, I'll tell you a couple things I do know.
So there's a work from the 90s by Gail C. Murphy and David Notkin, which I like a lot.
It's called Reflection Models, Reflection with an X. It's basically about this interactive
tool for helping programmers build a map of a code base in their head.
So it starts like this.
You look at a code base and you say, okay, I think this part is IO, I think this part
is data management.
And you just mainly write these ideas down.
It's like, this file is I-O. It says, okay.
And then based on that, it generates a component connectivity graph
where it says, all right, here's everything you said was part of the I-O code,
and then it draws lines between them for, I think, basically call graph
or data references.
And you see, oh, this function,
there is this subcluster within that
which is very heavily connected,
or this thing, I'd say it was an IO,
but it has all these interactions
with the data management part.
And then from that, you can refine your map.
You can break things down to finer components,
move things around, try again. And so they had a case study where a guy at Microsoft, he was trying to do this
big Excel refactoring project. He saw the talk, tried it at work, and after four weeks,
he said that he had gone deep enough understanding of the Excel code base that he thinks would have taken him two years without the tool.
So this is super cool and exciting.
Yeah.
So they built two different tools that did reflection models, one for C++, one for Java.
And you can get neither of them today.
This is my go-to example of ways in which tool development
is behind in some ways where we were 20 years ago.
Got it.
Okay, that makes sense.
I guess I just wanted to try one thing one thing out on you and then we'll
continue with your story so um the research that i that i'm working on now is on i i think it's in
the field of program comprehensibility but i'm taking a very different approach uh my approach
is that the languages we have today don't lend themselves well to comprehensibility so like we
need to vastly restrict what's allowed to do.
For example, no go-to statements would be something that I'd want to kick out of the language.
So anyways.
As a little side note, I don't think go-tos are...
They're interprocedural go-tos.
I don't think they are that important for comprehensibility.
And that if it doesn't affect the interface of the function, then
it's a local problem. It makes one function harder to understand, but not the system.
Yeah, that's fair.
Yeah, but it turns, it
effectively turns that local function into a black box.
Potentially. Yeah, which is okay if you're okay with it.
I'll point out that in the systems programming world, it's pretty widely known
that that goto is the best contract in the C language for error handling.
So it's still very much used.
It's still very much used.
Interesting.
Well, I guess my question here is,
it seems like you're very much taking this polyglot approach
where your thought is that tools are too expensive
to build for one language.
So if I build approaches that allow tools to be built for any language,
then that kind of solves this problem.
My inkling or whatever, my predilection in this,
my tactic is to just build a language that's just better.
And then somehow build a language that's just better. And then somehow,
like build a language that lends itself to tooling better.
So that's how I decrease the cost of creating tools.
Yeah, I think definitely some people will share that idea.
A very extreme example is there is a language
called Parasail, which is created by Tucker
Taft.
I saw a presentation about it many years ago.
It basically was designed to be a very restrictive language.
I think it might not even let you do recursion.
The idea that it would be so restrictive that it would be very easy to write tools for
and very easy to super optimize and parallelize automatically.
So that's the relatively extreme approach.
Got it.
Cool, yeah.
So yeah, definitely some tools are easier,
some languages are easier to build tools for than others.
That's one of the reasons you see so much academic work focusing on Java.
And Java compared to C++ compared to Python is pretty tame.
You can do a lot of things just with a bytecode, which is pretty well designed.
Okay.
And obviously there is a lot of old stuff in existence and it's not going away
yeah yeah yeah that makes a lot of sense but particularly from your experience at semantic
designs yeah you you you're like intimately familiar with you know massive code bases of
dead languages yep or i guess the other way to put it is that, like,
programming languages don't die.
Yeah.
If you get a pay stub, there is a pretty high chance
it was processed by ADP and went through a large
40-year-old COBOL system.
Actually, not COBOL, assembly for dead language.
Probably a lot of other things will go through COBOL systems.
The company I started, we use ADP, so there you go.
Cool, so let's stop talking about it.
Let's talk about kubix.
Tell me a bit about the idea and what your novel approach is.
So kubix is my framework for language parametric transformation. The tagline is
one tool, many languages. So, here's a motivating story for why this is important. When I was
at Facebook as an intern in 2010, they had a lot of privacy bugs. And it was things like, so in Facebook,
you can upload private photos or things
only your friends should see.
And you go to someone's photos, they view photos,
it would fetch the photos, it would
check which ones you can see and show you those ones.
And that is error prone because you
need to remember to do the same thing any other place we give you the photos
One place where they did not do that is in the report profile button
So if you went to report someone's someone's profile, it's a this profile is bad because of its photos
It has an illegal photo
say okay, which photo do it report and there you can see all the photos.
And they had a lot of other things like this.
Another thing was for a few hours, there is the button that says, how does Bob see my
profile?
What does it look like to him?
For a few hours, you press that button, you were actually logged in as Bob and see his
private messages.
Wow.
So they needed to do a whole program refactoring in order to fix this. And their
idea was to an architectural change. They would create a viewer context object, which, and instead
of having to remember to every time display a photo check whether it should be visible,
instead you would move all that to a central location.
When you get photos in the database, right there you would pull out all the ones which should not
be visible eventually. So they needed to pass through this viewer context parameter
through many, many, many tens of thousands of functions.
They took a couple dozen of their best engineers off their other projects. They walked them in a conference
room called the candidate room. So this is the candidate team. Basically, they spent
every waking moment for several weeks just adding just these parameters to function calls.
And so as an intern, I had this piece of the code that no one else was working on. I wake
up one day and get a merge conflict. Like who else worked on this? These people did. They added some parameter passing. So
basically, a massive amount of effort adding parameters to functions throughout the codebase.
In 2014, Dropbox had a different problem with a similar solution. Right now I'm logged to Dropbox on my PC.
I actually have two accounts on this computer.
I have my personal Dropbox.
I have my MIT work Dropbox.
They also needed to add an account parameter to many thousands of functions in order to
implement this.
They obviously did a lot of other things too, but that was a large part of it.
And I'm told that it was the number one project for the year 2014. At the time, they had over 100
engineers. Two companies, massive change, adding parameters to functions. This is the kind of thing
you can hire Semantic Designs to build a magic button that does for you. But if both of them
had gone to Semantic Designs or hired some other
pruning transformation experts,
they would not have gotten the economy
of scale from having the same problem twice
because Facebook,
PHP, Dropbox, Python.
So this wouldn't
be solved via the
multiple front ends that Semantic Design has?
They'd have to build the same
thing separately.
Maybe they could share some code, but not that much.
Got it.
But with kubics, you could build the same tool for both languages, and in fact,
we did.
So in our kubics paper, we built a prototype of a tool that does this.
And in the space of less than a week, we built this prototype and got it running on C, Java,
JavaScript, Lua, and Python, which are the five languages that Qubics currently supports,
not yet PHP.
In one week?
Yeah.
The prototype is not that sophisticated, but the big thing is that we are still sharing
most of the work between languages because we can using the qubits approach.
Got it.
Okay, so yeah, so what's the approach? How'd you do it?
It's called incremental parametric syntax.
And it's basically
a recent existing idea, but with a small addition that makes all the difference.
It's an extension of some earlier work on modular syntax, particularly something that's
well-known in the Haskell community called data type solid cart. Data type solid cart lets you say I'm going to find a programming language
in terms of different fragments. Here's my control flow fragment that has while loops
and if statements. Here's a fragment that has go tos. Here's a fragment that has functions.
A fragment that has plus and minus. And I can put these together into a language. And I can define a transformation
or an analysis that runs on each of these parts separately. And when I put them together,
I get the transformation analysis for the whole language.
Some people took this, made it better, made it easier to scale. But they still have the problem that if you wanted to do this for C, what you would do
is you would basically take every piece of C, the C spec is about 100 pages, which is
small for a language spec, and you'd go and implement every single part as a generic fragment.
So you'd have a generic function of fragments that handles C, and hopefully some other languages.
You'd have generic pointer fragments, every single thing is in C. You'd somehow need a
model generically.
And this is very hard because basically if you had generic functions which would be used
in C and Java, you would somehow need them to be the same, which they're not.
So this approach couldn't really handle mismatches between languages.
Whereas, I say, language parametric transformation, sharing code between tools to different languages,
it should be possible because languages are similar, but it's hard because languages are
also different.
Yep. So we made a small addition to this idea called sort injections.
Where basically I can now say things like here is a generic function declaration and
it has a list of arguments.
But what an argument is differs by language.
And I have a way of saying for this language, this is an argument.
Similarly, for variable declarations, I can say a variable declaration is a list of left-hand
sides, is a list of declaration binders.
Each declaration binder potentially has some attributes.
And then for each language, I can say what those things are. So in C, a declaration binder could
have a pointer. In Lua, you can declare many variables simultaneously. You can say var x, y, or as a list.
You can do that in JavaScript, too.
In Java, you can have attributes on each declaration.
You can say int x, y brackets.
And x is just int.
Y is an array.
I'm with you.
This makes a lot of sense.
Yeah.
Whereas in JavaScript, you don't have that.
So we have this nice thing where different languages
share common parts that you can write against.
But you can still capture all the indistinguishable
of each language.
Cool.
Yeah, this makes a lot of sense.
So I just want to backtrack a bit.
I have two questions.
The first one, so you mentioned this data structure is a la carte thing, which sounds
fascinating.
I, and I imagine a lot of listeners, are pretty familiar with Haskell.
So could you give, can you explain how this is in Haskell?
Sure.
So, and this actually is a new name for an even older idea from the term rewriting community called Sum of Signatures.
And I think the best way to understand it is to take a step back from it.
So, Haskell has algebraic data types.
You can say data tree equals leaf of int or binary node of tree, comma, tree.
You can take a step back from that and look at the theory of how these are defined.
And they actually are defined in three separate phases. So first, each of those things like leaf,
node, those are called constructors and they're just tuples.
So a leaf has an int, it's a tuple of ints, a one tuple.
The interior node is a pair or a triple, say it has two children and another int.
So that's the first step, is getting these constructors.
Then you can say a tree is one of the many constructors.
That's a sum.
It's an or.
And at this point, I've said, really I've said these are what my nodes look like, but
I don't have the trees yet. So, maybe I say my language
has an add node and it's going to add two expressions together. What's an expression?
I haven't defined that yet. The point at which you define what the expressions are is when
you do a fixed point. When you add recursion to the data type, you can say, feed this thing
back into itself.
So when you just have a list of different node types, but you don't know what your children
are, this is all very modular.
It's only when you add the fixed point or tie the recursive nods, let's say, that becomes
very tied to the current state.
So what data types a la carte does is it lets you program get signatures rather than term
types.
So you basically write things in a way of an add is a plus of two expressions.
What is an expression?
I'll tell you later.
And you leave a type variable for what the children are going to be.
And at some point later, you say, so I defined this list of nodes,
which is either an add or multiply or an if statement, and they have children.
What are those children? Now I'll tell you.
It's the thing I just defined. That's the recursive fixed point.
Okay.
I have a little more clarity.
So to speak it back to you,
it sounds like the sum of squares and the data types a la carte
are essential to the implementation
of algebraic data types?
In the actual type theory, an algebraic data type is defined from three basic type theory components.
Recursive types, sums, and products.
Got it, okay.
And like a tree with two nodes, that's a sum.
Yeah.
So the product is saying I have a type variable T, T cross T.
It's a pair of Ts.
What's a T?
It's a variable.
The sum would be saying int plus T cross T.
So it's either an int or it's two of these t things. I haven't told you what a t is yet. And then the final stage, say mu of t dot
int plus t cross t. So there you say, what is a t? It's a one plus a T cross T. That's a recursive type.
And when you actually spell that out, it becomes what's a tree?
Either it's an int or it's two ints.
Or basically, when you actually substitute one plus T cross T and
for T recursively, you get this infinite sum which describes every single shape of a binary
tree.
Got it.
Okay.
It's pretty fascinating.
There's a great reading on which unfortunately, Could send a I could send a link out because they announced go to web that archive for it's off the internet
it's called the algebra of algebraic data types by Chris Taylor multi-part series and
Shows you how you use point-o-meal manipulation on these out of break data types and find a lot of cool properties
Is it okay Okay, cool.
I'll add that to the notes.
There's also, there's another article which I think is a lot less good,
but it still exists on the internet without web.archive, which is shorter.
It's called the Algebra and Calculus of Algebraic Data Types.
It sounds like you're approaching cubics, the parametric.
Can you repeat the phrase for me?
Incremental parametric syntax.
The incremental parametric syntax.
It sounds to me like that's very similar to this idea of a universal abstract syntax tree.
But I know that that's an idea that you aren't so excited about.
So what's the difference?
Yes.
The universal AST is something that I love to hate.
So the idea of the common representation is that I'm going to create a single syntax tree.
And instead of having Y loops in C and in Java, I'm going to have one kind of Y loop.
And I'm going to translate all of C in Java and JavaScript into this one syntax tree.
I'm going to write things on this one thing.
And when I want C or Java back, I just output them.
This is how most program analysis tools work.
It works great for program analysis.
Architectures like GCC and Clang and LLVM, they'll do this.
They run GCC on Java or Pascal or Objective-C or Fortran.
You'll get into the AST for C or Fortran, do some stuff there.
Then I'll change them into, I think the IR is called Gimple.
I'll change them to some common representation, do some more optimizations.
And then from this IR, you'll pick a code generator off the shelf and get your x86 to your arm.
But there's two big things wrong with it.
So one is that I said before the power design is not what it can do, but what it can't do. And it's very important
not just what you can represent, but what you can't represent. So, there are a lot of
when you're reading a Java program, you have to read it differently from a C or C++ program.
In that, if you see a local if you have a local variable x, it's an int and you call a function with x in Java,
you know that x has the same value afterwards.
In C++, that function you call could actually take x as a reference and then modify it,
and now you have a different x in the calling function afterwards.
So the presence of pointers, and especially especially references makes a lot of programming analysis things
difficult.
Both computers doing programming analysis, but also just you reading the code.
The knowledge that your programming language is not a pointer or not a pointer arithmetic
is very powerful.
It's just absolutely lost.
That's part of the reason why all these different infrastructure, they will do some
of their authorizations at the language specific level before going to the IR.
But the other big thing is that when you want to have a perm analysis, it's easy.
You set everything in.
It's okay if you lose information as long as you have enough to still produce useful bug reports,
whatever you're trying to analyze for.
But in source-to-source transformation,
well, if someone types in 0x10 and they get out 16,
they're going to be mad,
especially if the prune transformation didn't touch the part of their code.
So you need to keep all this information
about language-specific stuff around.
It sounds like that's exactly what you're doing with kubics.
Yes. Yeah.
So in kubics, we don't have a single representation
for C and Java or JavaScript.
We have a family of representations.
Each of them shares common pieces,
but each one specific to that language
and able to store it or track everything.
So hold on.
So to me, it sounds like in Cubix, like if I have an assignment in Cubix,
that's a data type that is very general,
that can handle all the quirks of every language.
Yeah.
So we have a general assignment data type in Cubix,
but it has custom pieces per language.
So an assignment is general,
but a left-hand side is language-specific.
But even still, there's some common left-hand side.
So every language has a left-hand side
that can be a single identifier.
Some languages have more, but everyone has an identifier left-hand side that can be a single identifier. Some languages have more, but everyone has an identifier left-hand side.
And so I can write a prune transformation that can see
an identifier left-hand side and do something, and know it's something
for every single language.
Got it. Okay. So to me, it sounds like
that is a universal AST.
It's just not what people thought it would look like.
You can call it that.
But the thing is, it's a different kind of type.
It's instead of saying, I have a type IR,
I'm going to convert all my representation to that
and convert it back to the original representations.
I have a parameterized family. So I write my types in a different way.
And in the talk I gave on this, you can see a recording of this talk on YouTube that I gave to the Boston Haskell group, as soon as you declare the type signatures, saying I can
invert things to common IR and then back to the different representations, you've already
lost.
And one reason is that when you write down those types, you're basically claiming you
can invert any language to any other language, which is too good to be true.
So the catch is you get things like...
So there are more than zero frameworks that do try to let you write...
They do use common AR and do try to let you write transformations.
And all of them will mutilate the code.
So one example is the Compass-Royce framework from...
I think it's either Lawrence Berkeley or Lawrence Livermore. I think framework from Lawrence Livermore.
In their manual, they have an example of a programming transformation written on C. And
it converts all the for loops into why loops, even though it should have been able to leave them the same.
Why? Because they use a common IR.
And this IR does not have for loops and Y loops separately.
It just has Y loops.
Got it.
Yep, that makes sense.
I have a friend, Aiden Knief.
I don't know if you know him.
Yeah, I had a Skype with him the other month.
Cool.
I'd be curious to get your thoughts on his approach.
Let's see.
For anyone who didn't listen to the episode I did with Aiden,
he's building a tool just for one language, but it's code generation.
So you would explain to his tool how you want to write backend routes for your express router.
And then it could generate, if on the front end you need some data, it could generate the backend route for you automatically or vice versa, that sort of thing.
Yeah.
So it actually is from local languages.
Well, I don't think it's from one language to another.
No, it's not.
So the interesting thing about it, I think, is just identifying the problem.
He found the problem, which is practical, but also relatively simple to solve with simple
technology.
I think this has a lot of potential.
My big point of skepticism is the flexibility of their matching. Which is they're basically using tree patterns,
which are a pretty easy thing to come up with. And it is very easy to overestimate.
To think this is what code for doing this should look like. Very easy to underestimate how much variability you can see in that.
So whenever you get into application where it's important that you find every case where
something happens, or even 95% of the cases, then pretty soon you need much more sophisticated
technology.
But I did tell him some of these problems.
I told him to look into associated commutative matching
as something which is very important,
but it takes some architectural changes upfront.
So, and I found him pretty humble
and hopefully he'll be able to do some great stuff.
Cool, sounds good.
Um, be able to do some great stuff. Cool. Sounds good. Okay. Cool. Thank you. Let's talk about the coaching business you run.
Sure. So yeah, maybe give a, I'd be curious to know, yeah know how it got started. So a fun story.
Back when I was an undergrad,
I was deciding whether I wanted to do software engineering programming languages research with Donovan Aldrich
or to do cybersecurity analysis of binaries research
with David Bromley.
David Bromley has become very famous.
You might have heard of the DARPA Cyber Grand Challenge,
where basically people program computers
to hack into each other
and stop each other from hacking into each other.
They ran a contest at DEF CON.
His team, for all security, won that contest.
So he has some pretty awesome stuff involving binary analysis.
So I was choosing which professor to work for.
At first I chose Brumley.
So I was going to take the elevator back to my room to send the email to announce this.
And then I changed my mind to the elevator for a very bad reason. I thought, oh, maybe if I worked for Jonathan Aldrich
software engineering research, that will suddenly make me a better software engineer. And then
I'll be able to do stuff faster. And of course, that was silly. But the funny thing is that
over the next many years, it actually became true. And that as I got a deeper understanding of software engineering
and being able to see the preconditions and post-conditions of functions,
I actually discovered I'd become very good very fast.
It became very obvious when I joined Aptimize.
The person who replaced me at Aptimize, but who said my code was like a poem, like one
of those beautiful Haskell Bermuda you see, except that this was Java for Android.
That code base has managed to survive very well, easy to make major, done some very major architectures to it in relatively short periods of time, pretty low bug rate.
All things are pretty important, especially given that we are under very heavy constraints to make something with few bugs.
Because if something goes wrong, it's an SDK that other developers put in their apps.
If something goes wrong, first, our customers have to upgrade their version in their app,
and then their customers have to download the new version.
So you want to get things right.
So I actually stayed on as a contract for Apptimize in grad school.
I started making some extra money on the side.
Didn't like living like a grad student.
And I had one instance with a coworker where she had committed some bad code.
People were criticizing it.
She didn't understand what was going on.
And I sat down with her and taught her some basic,
taught her some of these soft engineering concepts, which are some things that I think some people have intuition for, but are pretty non-obvious.
Not many can really explain very well.
And that made a huge difference for her.
I just realized how satisfying that was and also how impactful that was to make her better.
Much more impact than I could have in an hour just doing it myself.
So about two and a half years ago, I started a company doing this professionally.
My first client was someone who had just been fired from their job.
And a few months later, they were a totally transformed person. And suddenly, we're getting
offers left and right from Google and Facebook, and now are quite senior at the company and
having like fresh Ivy League grads look up to them. It's a huge transformation for this
person. So, yeah. So, over the years, I crystallized a number of different teachings, which are largely inspired
by my knowledge of the academic side. Basically, the way I look at it is every time someone writes
some kind of program analysis or synthesis tools, there's some insight behind that about how software works.
My goal is to teach this intuition without having someone having to spend years studying
type theory.
There is a lot of stuff I'm going to talk about in my strange loop talk which is coming
up in a couple weeks.
One example I like right now is if I have a function called sanitize
that escapes all single quotes, and what you're really doing is that you're creating a notion
of a sanitize string which is separate from this function. And this idea of a sanitize string can
be hidden. Meaning that if I were to write my own code which sanitizes the string exactly the same way, then there's a sense in which it's not legal
to use that because the notion of sanitize is opaque. And this is something which you
could write down very clearly in a theorem proving language like quk. But it's something
which is almost kind of mystical and ethereal, you just look at normal code,
because you're talking about properties and abstract ideas that are not present in the
program at all.
Yep.
And in fact, so much of what makes good software involves working with ideas that
are not present in the code directly at all.
Something in the design space which gets lost when it's
turned into code. And it's kind of interesting that this is an idea which sounds almost kind
of crazy and mystical in programming circles. But in other circles, it's just mentioned
offhand, something obvious. So, in a thread that I'm in the other day, there's a discussion, it's like this famous
researcher says you should document the module by saying what secrets it hides, what decisions
it's made that can change and no one else can care about, can know about.
And it's often, oh, but this is much harder to extract in the code if it's just not present.
So you mentioned that how in a language like,
I think it was Coq was the language you mentioned?
Yeah.
You can communicate the high level notion
of what you're trying to do?
To some extent.
And you can write things like, this function
must take a sanitized string.
This function makes a string sanitized.
And set it up in a way
where the only way to create a sanitized
string is to call that function.
And if you write your own function that
sanitizes that string, then it's not
sanitized. You might have replace
all single quotes, but you don't know
what sanitized means.
Oh, well that I
understand, but how about
a formal definition for sanitized to make sure that the definition of sanitize is awesome?
In Coq, you would actually write down a formal definition of sanitize.
You can make that a private definition.
And you can prove that your sanitize function satisfies the definition.
But you can't prove that for any function in another module.
Yeah, and so it's two parts.
You describe
the
ideal.
The thing that's ethereal in most programming languages
is you can make it explicit in Coq
and then you write the implementation code
the lower level code and then you prove it.
In some order, yeah.
Got it, okay, yeah. You can do it in some order yeah and got okay yeah you could do in either
order but but they go hand in hand um yeah and i see the proof as less important from a design
point of view but just having just having idea that there's this some kind of concept which is
not present in the compiled in normal source code, but is present in this
design space is very important.
So is the solution that we should all be like, we should have the language, we should bring
it to our code.
Like we should bring that feature from cock to like every language.
That possibly.
And so there already is some variation in this. Like, in Python, it's actually
very non-straightforward to define an interface in the Java or C-sharp sense, and saying that
these are the functions which other people should be allowed to call. Here's an abstract description
of what they do, which is separate
from any specific implementation.
You can do that in Python, but you almost have to work around the language to do it,
whereas it's just very natural in Java or C Sharp.
So there's a lot of things in that spectrum.
But in my business, the angle I focus on is more just teaching people to think
like this whether even if they are working in python or ruby got it that makes sense
i think i think that's a great stop gap uh just in the interest of improving programming at the
like the language level i'd be interested because i think a lot of my best insights for improving
programming are taking things that I've taught to students very
instructionally, like, like the way you're doing, but, and then come up,
and then come up with creative ways to embed them into the language themselves
so that they don't even have to be taught because embedded.
So with that in mind,
I'd be curious to hear your thoughts on like almost
like higher level languages where you can retain more of the abstractness of your idea uh so like
you're not losing because when you uh like as i've seen you write in other places when you
um write an implementation for a high level idea, you necessarily lose a lot of the notion
of what you're trying to accomplish.
Yeah.
And so, yeah, I feel like you're someone
who would know the like state of the art
on what a higher level specification language
would look like.
Because I have no experience with those languages.
Yeah.
So I've done a medium amount
with a number of different specification languages.
I don't have experience using any of them for deployed systems.
For that, you might want to talk to my acquaintance, Hila Wayne,
who is another blogger and expert in TLA+.
Got it. Okay.
And I think you mentioned somewhere that you were familiar with Sketch,
which it sounds like is related to this idea.
Oh, yeah.
So my lab built Sketch.
So Sketch is very much not something...
Okay, I guess it kind of lets you express your idea at a higher level.
So Sketch is a general purpose program synthesizer. And its big thing is the idea of a... A Sketch is a program with holes. So some of the early applications were, you say, things like, here is a very, very dumb implementation of linkless reversal.
Which is like, you just remove stuff and add it to a new list.
And then you want the more efficient version of linkless reversal.
So you say, okay, I know it has something, it's going to involve a while loop and it's
going to involve some combination of these assigned operations. Here's a soup of possible things you can do.
Here's a reference implementation. Go find some way to put these together into a program that
does the same thing as a reference implementation. And it would. The way it works, it gets super
nitty gritty in the implementation. And it actually does some bit blasting, which means it creates a difference.
It creates, it basically looks, expands your program out to a static graph,
unrolls your loops, looks at all the bits that can be in your program,
and does solving techniques on the bit level.
Oh, wow.
On the implementation side, it's super nitty-gritty, not very abstract at all.
But from another perspective, you can use it to raise the level of abstraction and say,
I don't care about the details.
I just care that it does the same thing as this reference implementation or it satisfies
these properties.
And here are some constraints on how it works.
Okay.
That makes sense.
I like to end with an opportunity for you to mention ways, if you're interested in being contacted by people with questions, ways to contact you, places you produce things on the internet, anything you want to get out there or want help with. So, my blog is pathsensitive.com.
I mostly blog about advanced ideas related to software design.
You can also check out my papers on Google Scholar or on DBLP, the computer science paper
database.
My coach business is jamescouplecoaching.com.
I offer one-on-one coaching
as well as a web course
and in-person courses
to individuals and companies.
The next Advanced Software Design web course
is starting on October 11th.
I actually have a special offer
for listeners of this podcast.
So if you go to jamescouplecoaching.com
slash Steve,
then you'll get a special offer, a special bonus if you sign up for the course.
The course is six weeks.
It'll basically take you through a number of big ideas about software engineering, things
that are intent, that can change the way you view code but are not very well known.
You can check the testimonials.
It's gotten some pretty amazing results.
People saying it made them way better, including both junior people and very senior people.
It's done in a pretty structured way, where both you'll do pretty low-level drills with
small code examples, and then it'll also give you a walkthrough of a large source code base.
And I'll show you how this idea affects this real code base.
And something else entirely, when I decided projects for way too long, has been the area
of binary modification.
My other area of expertise, which I haven't talked about at all, is in how to modify programs
without a source code and in reverse engineering. So at so project iron fist is a game mod and for the classic pc game here is a might
magic 2 that i've worked on with some international collaborators for a long time yeah you can check
it out at iron fist i-r-o-n-f-I dot S-T, and see how we work our magic.
And of course, my personal website, which I'll update sometime this year, probably,
is jamescouple.com.
Cool.
That's awesome.
Well, anyway, thanks again for coming on and talking with me.
I learned a lot.
Yep.
It's good to talk to you, Steve.