Posts

  • AI and PR reviews

    With AI doing so much coding, the bottleneck has become reviews.

    • What do we do with those pull requests with thousand of lines of changes that the AI has created?
    • Do we trust it and merge it? Do we spend the time to review it?
    • It might have taken 20 minutes to create the PR, but if it takes 2 days to review it, have we gained in efficiency?

    There are many axis we can use to evaluate a change; for example:

    • syntactical (typically easy to review, lower risk) versus semantical changes (typically harder to review, higher risk)
    • changes in the critical path (e.g. movement of money) versus less critical (e.g. backoffice changes)
    • reversible changes versus non-reversible ones (or reversible but incurring a large amount of clean-up effort)

    In of all of these dimensions, there are subspaces where I’m totally fine with large changes driven by AI.

    And there are some spaces where AI changes and AI reviewers should (IMHO) be driven and understood by a human.

    My wish is that AI will help us undertake the huge yet boring refactorings that we keep of pushing into the future; like replacing libraries that are old and deprecated but that we don’t dare to replace because the function signatures changed and the refactoring would touch thousand of lines in hundreds of files.

    These are the types of changes are large (boring to be made by a human), but easy for AI and easy for a human to review.

    What I really dislike is changes that are large and modify logic (as opposed to being syntactical).  Those types of changes (semantic changes) are hard to review and carry a lot of risk.  We need to try to break these down into paletable chunks that are digestible by a human.  I’m not quite ready to let an AI take care of banking accounting systems, for example. It’s too risky; it would be a recipe for disaster.

    Breaking it down

    When adding a feature, or making a modification in general, we need to understand which parts fall in which subspaces, and we need to craft the work and the pull-requests into chunks.  Each chunk fitting into one of these subspaces.  Meaning.. don’t mix a risky semantical changes together with a thousand of lines syntactical change!

    To me, a change is like a theorem you need to prove, and the implementation is the proof of the theorem. Bare with me for a moment:

    Modifications are typically like this:

    • we want a lot of the system’s behavior to stay the same. Call it C for constant.
    • we want some of the system’s behavior to stop existing. Call it S for stop.
    • we want to add some new behavior into the system. Call it N for new.

    So, today the system behaves as C union S. We want to make modifications so that the system behaves as C union N. That means, continue doing C, stop doing S and add N. Sure, sometimes we don’t have N, and sometimes we don’t have S, but in general we have both.

    We want to go from C union S and modify it and we hope these modifications will create a system that satisfies C union N. I’ll make a analogy between pull-requests (PRs) and theorem proving… but first a quick reminder:

    When proving something new, we start with a set of things we know, and we apply logical steps to the things we already know in order to grow the set of things we know. More precisely:

    • When proving something, we start with a set of assumptions. These are the things we know are true, or assume are true.
    • To prove something we take small deduction steps. Deduction rules are rules that were carefully crafted in order not to change the truth of a system; they can add truths we didn’t know before, but they can’t add a falsehood.
    • So we start with the assumptions, cleverly apply deduction steps, and hope to arrive at a conclusion.

    The analogy with theorem proving is the following:

    • Our set of assumption is C union S, meaning, it’s how the system behaves today
    • In order to get to C union N, we make small modifications starting from C union S. Each modification is provably correct (we can look at the modification and see that it is correct).
    • After a sequence of incontestable modifications, we arrive at C union N.

    C union S –PR1–> C_1 –PR2–> C_2 –> … – PRN –> C union N

    Observations:

    • A proof is the sequence of steps from a set of assumptions to a conclusion.
    • Stating a conclusion is not the same as proving the conclusion.

    Transferring that back to AI and pull requests:

    • I can see that a modification to the system is correct when I reasonably understand the existing system (the assumptions) and I can see the steps that lead it to a new system (the sequence of small PRs in between).
    • A very large PR that is not understandable is like stating a conclusion without giving any proof of the conclusion!

    Trusting a large PR created by an AI without understanding it is like trusting a conclusion without understanding the assumptions and the reasoning behind it.

    continue reading...
  • Late bloomer: Reflections on Becoming a Tech Lead

    I started doing low-level programming and architecture at Intel and Apple, then I got a PhD and dabbled in academia. But it wasn’t until I re-joined industry as a high-level software developer that became a tech lead. And despite my degrees and experience, I still felt uncomfortable with the role.

    The truth is, I have an ambivalent attitude towards responsibility. A former manager of mine once said I had an over-inflated sense of responsibility (I’m pretty sure he didn’t mean it as a compliment). I felt hunted by questions like, what if I don’t know what to do or make the wrong call? What if the circumstance demands my attention but I’m unavailable because I’m feeling disconnected, scared, or sad? Being an individual contributor (as opposed to a lead) allowed me to take on responsibility without risking divulging the ups and downs of my internal existence.

    I knew I was chickening out. The technical name for it is avoidance behavior. But eventually I felt like I could lean on my colleagues for guidance and moral support, and so I took the tech lead role.

    continue reading...
  • What I think about when I think about music

    Recently I started to learn the piano on my own. With an app installed on my phone, I began to learn from the very beginning. Eventually I bumped into questions about music theory, so I incorporated ChatGPT and YouTube as learning partners.

    I figured that being inspired by music in general would keep my motivation, so I started reading a book, :book: Absolutely on music, that is a conversation between the author and an orchestra conductor. One interesting thing about the book is that you can listen to the pieces they are talking.

    This opened a fantastic door for me, which I continue to explore with the help of ChatGPT even after finishing the book. Here is a collection of conversations with ChatGPT that I’ve found interesting.

    continue reading...
  • You get what you measure

    This is a short post on how measuring performance can backfire. So you are trying to foster a culture and you decide to measure how close (or far) your organization is from that culture. Although your intentions are good, your efforts can backfire. You can end up with a culture that is the opposite from what you intended in the first place.

    We will walk through two ideas which, put together, lead to the concept of “you get what you measure.” We then close with what this could mean for your organization.

    continue reading...
  • Go, Scala and case classes

    In this blog post, we will take a look at Go and Scala, and specifically, at their approach to case classes.

    One of Scala’s key features is its support for case classes. Case classes are meant for holding immutable data. They are similar to regular classes, but they come with a number of useful features out-of-the-box, such as the ability to generate a toString method, a copy method; they also come with matching support.

    Go, on the other hand, does not have built-in support for case classes. We will look at three different approaches that can be used instead.

    continue reading...
  • Yesterday I defended the PhD

    As cliché as it sounds, the story of my PhD started in my childhood. Growing up, I wanted to be a scientist. In my teenage years, I had my eyes on a joint math and physics program at the Universidade Estadual de Campinas (UNICAMP), Brazil. I started the program in 2001 and still remember Professor Alcibíades Rigas class on linear algebra and analytical geometry. Rigas used a graduate level book written in English as the main resource for this freshman level course—in a country where most don’t speak English. It’s an understatement to say that the class was hard. But rather than an exercise in gratuitous punishment, Rigas helped us build a solid foundation. I fell in love with the campus and the program, but I left midway through my freshman year. While taking entrance exams in Brazil, I had also submitted applications for college in the US. When notified of my acceptance at the Rochester Institute of Technology (RIT), I chose the unknown over a life I had been falling in love with.

    continue reading...
  • Concurrency, Distribution, and the Go memory model

    The Go memory model specifies two main ways in channels are used for synchronization:

    1. A send onto a channel happens-before the corresponding receive from that channel completes.
    2. The $k^{th}$ receive from a channel with capacity $C$ happens-before the $(k+C)^{th}$ send onto that channel completes.

    Recall that happens-before is a mathematical relation, as discussed here.

    Rule (1) above has been around for a while. The rule is very similar to what was originally proposed by Lamport in 1978. It establishes a happens-before relation between a sender and its corresponding receiver. Rule (2) is a bit more esoteric. It was not present in Lamport’s study of distributed systems. There is a good reason for that absence.

    continue reading...
  • Channels vs Locks

    With channels, we typically establish that two things are synchronized because A happens-before B. We often know the order in which they happen. In this post, we’ll see a use (or “misuse”) of channels. We will be able to establish that two things are synchronized, but we won’t know the order between them. We won’t know which happened first. We will then relate and contrast channels with locks: how are they different, how are they similar.

    continue reading...
  • What makes Go special

    goroutine What stands out the most in Go, to me, are goroutines and channels. The language was built for the many-core. It sprung from the observation that processors are not getting much faster at doing one thing, but are becoming faster by doing many things at once. To benefit from this trend, we ought to write multithreaded applications. Go makes it super easy to have multiple threads of execution. By prepending the keyword go at invocation, any function can run asynchronously (on its own “thread”). For example, the program below has only one goroutine, the main goroutine.

    continue reading...
  • Rhubarb tang

    If you are following the blog, you have been busy learning about memory models. We started from the basics: what are memory models? What’s interesting and challenging about them? Then covered weak memory and got to the point of introducing the concept of happens-before relation. With that, we visited a real-world memory model specification, that of the Go programming language.

    Pat yourself on the back. Great job! It is time to take a refreshing break. In my welcome post, I said I would share some (drink) recipes. Here is one for the summer. I call it “rhubarb tang”.

    continue reading...
  • The Go memory model

    The Go memory model starts with the following ominous trespassing sign:

    If you must read the rest of this document to understand the behavior of your program, you are being too clever.

    Don’t be clever.

    Feeling clever? Then come on in!

    continue reading...
  • The happens-before relation

    At the end of the previous post, we saw that a compiler can change the order of instructions in a binary (as long as single-threaded semantics is preserved). These changes can break synchronization, especially if we expect to synchronize by reading and writing to variables. When pondering about synchronization, it would be unreasonable to expect a programmer to peek into the compiler. Luckily, languages come with a memory model: a document that serves as a contract between the programmer and the compiler.

    Memory models are often defined in terms of the happens-before relation.

    continue reading...
  • Weak memory models

    In the previous post, we touched on consequences of our quest for performance. We saw that, by relaxing the order of execution of instructions, compilers are able to produce faster binaries, and processors are able to execute these binaries faster. We want this rearranging to be done for us because efficient instruction scheduling is a science in itself. Also on the previous post, we touched on the concept of sequential consistency.

    In this post, we will discuss single-threaded semantics, compositionality, and their relation to weak memory models.

    continue reading...
  • An introduction to memory models

    In this series of posts we will visit the concept of memory models using “real-world” examples. We will look behind the scenes and into a programming language’s implementation. We will see how the specification of the memory model relates to the language’s implementation.

    Below we cover some basics: what is a memory model and why do these models matter. We will touch on concepts associated with multi-threading and synchronization, such as the concept of sequential consistency, weak- or relaxed-memory, atomicity, etc. In future posts, we will discuss the Golang memory model and we will look into Go runtime source code. But first, a confession.

    continue reading...
  • Starting with a fizz!

    Welcome! I want to share with you some things I’ve learned about computer science. I hope to spark conversations, meet people, and have a bit of fun along the way. I will try to strike a balance between practice and theory, rigor and accessibility. I hope you’ll enjoy.

    My first “serious” post is about memory models, but before we start, here is a drink recipe that is as much revealing about my origins as it is tasty.

    continue reading...

subscribe via RSS