LLM-Driven Programming: Oh, really?

As much as I would like to be, I am not a computer science academic. Not at all.

I took a different path though.

I’ve been spending a big chunk of my days reading, writing, changing, and debugging computer programs for 25 years now. During this time, I’ve earned a technical degree in Data Processing, sat on a Computer Science undergrad course for more than 3 years (before drop out on the 4th year, a semester or so before graduate), and later on ended up pursuing a postgraduate degree in Data Science & Big Data, after earn a bachelor’s in Nutrition.

In those last 25 years, you better bet I’ve read a fair bit of books, articles, blogs, and whatnot, in an honest effort to educate myself. Still, I feel myself so undereducated to offer opinions outside my field of expertise ⎼ i.e. architect software systems for the Internet.

That’s why I always look for people who know better than me. So I don’t embarrass myself for free on the Internet. It might not be cool but it is intellectually honest. I’ll take it.

LLM Hype

As of late there has been a hype on the subject of LLM (large language models, e.g. GPT-3), especially ChatGPT. You must be living under a rock to have not heard about it. This thing is everywhere. People are freaking their minds about it. Some loving, some hating, and everything in between, I guess.

Here I am.

Even though I got a postgraduate degree in the subject of data science, I’m far away from being an expert in LLM. Far far away. Not even in the sight of human eyes. So my thoughts on it are based on my observations throughout the years and a fair share of real world experience as a software engineer. Take it with a grain of salt.

What do I think about it?

I think it might be a handy tool in your programmer toolbox. Have it there, as you have Google and Stack Overflow. That’s it. Simple as that.

https://commoncog.com/content/images/2018/12/Paper.Commonplace.37.png

I mean, let me elaborate a little more on this claim.

When I first got into programming, I had to buy books and magazines or borrow them from school and public libraries; also, I had a good buddy at work that taught me a lot about Clipper, and that was it. Then I discovered the online discussion forums (e.g. GUJ). And then I got to know about and attended to user groups meetings (e.g. SouJava). And then it was Stack Overflow, GitHub, and the plethora of technical content there is all around the Internet ⎼ you know what I’m talking about, just Google whatever about the most esoteric programming language and there you have it.

Those things weren’t mutually excludents, they were adding up one upon another, like bricks of a building. Or should I say a skyscraper? Go figure.

Anyway, I guess what I’m trying to say is that, even though I’m able to search anything on Stack Overflow (or rather on Google and then point & click it), read blogs, watch all sorts of interesting talks on YouTube, and so on and so forth, I still buy books and read through them. One thing does not exclude the other.

The same thing applies to LLM tools.

Be An Education-First Programmer

I would like to make a point here: I am a firm believer in “quality” education. And by that I mean attending formal university courses and reading “fleshy” books.

I know it is not a popular opinion nowadays, with all the bootcamps, quick video courses, and nano contents being heavily advertised out there, as if they are the penicillin of the 21st century, but honestly I couldn’t care less. Solidifying knowledge that passes the test of time calls for a strong education. So take your time, do the hard work, get past the quick tutorial, and get educated yourself.

Keep in mind that with just shallow knowledge you might not even be able to discern whether a Stack Overflow answer is right or wrong; and if it’s right, and if it works as expected, why it does. And in this case, what would you do if it doesn’t work? Trial and error ad nauseam, right? Which brings us to an indisputable fact.

Nobody likes that guy/gal in the office that asks questions all the time and wants to get every piece of information chewed up to just swallow it in without having to drop a sweat. I certainly don’t.

Let me tell you: copy & paste driven programming has been a thing for quite a long time, at least since we could find candidate solutions for most computing problems we search through the Internet. This is nothing new. People have been doing this thing for ages now. Much more after the advent of open source software and powerful search engines. And there is nothing wrong with copy & paste code from the Internet. Nothing. Well, at least until it does.

After all, we can copy & paste small snippets of code here and there, sample usages of APIs that we are not too familiar with, or that we haven’t used yet, or that we are getting a quick start. We all have done that and will still do. (Regex? Cron? Yeah, I feel you.) However, if we do not have a solid base on programming and software design, if we do not take our time to understand what we’re doing, we will end up in a frustrating loop of trial and error. Worse than that, folks, we will end up introducing hard to find bugs, performance issues, and security vulnerabilities in our software ⎼ which by the way, most of the time isn’t even our software but our employers’ software. Think about that for a second.

There is much more in software development than writing an SQL query, or building an API endpoint, or calling a remote service from a mobile app, or coding a depth-first search. A complete software solution is made of much more than that. Software engineering, as a field, is what’s concerned about that. Hence my point here is, if you don’t know what to ask for, what sort of answers do you think you’re gonna get? And will you be able to discern the right answer for your particular case?

As the saying goes, Garbage in, garbage out

When you read through answers on Stack Overflow, you better be quite sure about what you are looking for, to be reasonably assertive ⎼ there is a lot of junk content out there too, mind you; and they don’t come with a label “garbage”. Then you must take into account all the context of the original question and the provided answers, skipping the garbage here and there, before you can make a decision of copy & paste some code snippet as-is or write your own solution based on the built knowledge ⎼ e.g. the specific problem you’re trying to solve owns its particular edge cases. And this very thing, over and over, will add up knowledge to you.

See where we’re going? See my point here? See how ChatGPT can be a trap? You ask it, you get an answer. An answer so confident that if you don’t have a wide and solid knowledge of programming, you buy absolute horse crap without even knowing it. I’ve seen quite a lot of that with friends on Twitter and whatnot. Even with widely known algorithms.

As such, this “much more” is the thing we should care about (beside the basics) and is what we must focus on (provided we got the basics in place). Which brings us to another merciless fact.

We don’t know what we don’t know. And the only thing we can do about that is acquiring new knowledge, again and again ⎼ broad first, depth second.

Fundamentals of Software Architecture, Mark Richards & Neal Ford

This way, hopefully, we will know what we’re looking for and have at least a grasp of the answer when we find it.

Broad knowledge gives us a direction to go search for answers;
Deep knowledge helps us discern among found answers.

Okay. Cool. But how do we do that?

For broadening your knowledge of what’s out there to know, I think blogs and YouTube videos are great. They are short content that you can consume on a daily basis without breaking a sweat. But for deep solid knowledge, in my opinion (N=1), there is nothing better than reading books and official pieces of documentation. And if you can, if you haven’t yet, get yourself a seat (physical or online) on a university course (graduate or postgraduate accordingly).

Again, not a popular opinion. But I’m not popular myself, so who cares?

Enters LLM tools

Nonetheless I have several unpopular opinions, I think this one will hit the parade: LLM might be an awesome tool to help you gain solid knowledge. How is that? I’ll tell you how.

Start by asking ChatGPT what has to be known about, let’s say, mobile apps development. Then ask for short tutorials and YouTube videos to get the big picture. Next, ask for highly recommended books and lectures about it. Because ultimately you want to read several deep articles, from different authors, with different points of view, to get a solid understanding of the topic you’re studying.

That’s for one thing. Another thing it might be helpful is when you’re reading through a book and get stuck by lack of knowledge of a given required subject. Ask ChatGPT to sum the said subject up for you, so you get a base before going ahead. Same thing you would do with Google, reading one or two sources but quick and right to the point. Yet, afterwards, go ahead and get deep into the said subject if it’s something important for the general matter you’re studying. It’s okay to do that.

And yet another circumstance it might be very helpful is while you’re up to develop a software application for a given domain you’re not too familiar with all the ins and outs. I saw that the other day and found it fairly smart.

Let’s say you’re going to develop a fitness mobile app, because you’re into fitness and are super thrilled with the idea of helping people get healthy and everything. Okay. But you don’t know all the apps there are out there, and their most loved features, and how they are monetized, etc. These are very objective questions. So you can ask these questions to ChatGPT to get started quickly. You ask about app requirements. You ask it to, for example, write the welcome email you’re gonna send to new subscribers of your awesome fitness app, because you’re not particularly good at this. Right. Now what? If you understood what I’ve talked about until this point, you already have a good sense of what you should do next. You improve it, you make it better, you put on your special sauce. In other words, break the inertia, which is the arguably most important thing in this case, then make it particularly special ⎼ otherwise, all you’ll have is generic stuff to sell out. Not cool, I guess.

If you get access to GitHub Copilot and it’s no harm to use it on your work, you can ask it to scaffold pieces of code to you, like a method to parse a file, a repetitive test case, or whatnot. You know, that boring stuff that you have to do every now and then and can’t be fully avoided with proper code design and smart libraries. Nothing wrong with that. You don’t get extra points not doing so ⎼ as long as there is no policy in place where you work.

There might be many other similar applications, mind you. Think about that.

Oh, wait. I thought about the greatest benefit this could bring us: Do you know those guys that like to post on forums asking for solutions to their schoolwork? Like, you know, you read the question and it’s almost the literal assignment statement. You’ve probably seen that before. Yeah, now they can ask ChatGPT for that and leave us alone. Everyone is happy about it.

Will LLM replace human programmers?

No. I don’t think so. I am very skeptical about “X is going to replace human programmers”. Since I started with programming in 1997, I’ve already heard my fair bit about fads like that.

It will likely change the way we get educated on programming, how we overcome blocks when we get stuck while programming, how we deal with boring stuff, but replace us, homo sapiens, for more than CRUDs and mundane widely known apps, no, I am very skeptical about that. I might be wrong though.

And know, finally, this article comes full circle with what got me motivated to write it down.

Yesterday I had the pleasure to stumble on an amazing article by Amy J. Ko, which was a real joy to read. First because I agree with mostly everything she wrote there. Second, because unlike me, she is an academic and has been following this subject matter for more than a decade. So she definitely has one thing or two to collaborate in this whole discussion without embarrassing herself.

So do yourself a favor, cut through the hype, and read these outstanding articles to get yourself a better sense of the topic from different point of views:

Large language models will change programming… a little
Large language models will change programming … a lot

There is no TLDR; there. Take your time, enjoy the ride.

Now to end this long piece with a funny thing: I used to watch people make fun of academics all the time, saying how academia is far away from real world software development, and then, all of sudden, people are amazed and freaking out about a thing brought to life straight from academia research. What a big piece of irony. What a good time to be an academic.

Anyway, see you next time, folks.

Be careful with the hype.

A Relational Analogy for the Cassandra Data Model

The idea here is to use Twissandra to try to clarify the column-oriented data model employed by Cassandra, which goes beyond the simple key/value model and often is misunderstood. It’s not quite hard to understand, if you have a little familiarity with JSON-ish data structures, but is quite a bit if you don’t. On the other hand, almost everybody knows relational data model. So here lies my idea on take the relational data model as an analogy.

Wait! I don’t pretend make you think that one can use Cassandra in the same fashion that a relational database. Instead I’d want to get you introduced to that paradigm and give you some ground to further learning. Hopefully, you’ll have insight on potential use cases as well.

Here we go!

Let’s everybody to the same page

Twissandra is a sample application which aim is to demonstrate how to use Cassandra. It’s essentially a simplified Twitter clone, as you can see at http://twissandra.com.

I gently ask you to take a look on the project README, at the specific topic on Schema Layout, in order to getting knowledge of its data model (i.e. users, tweets, friends, etc), because that is going to give you underlying knowledge to follow the analogy which follows.

The Analogy

I believe that at this point you have read the schema layout of Twissandra. So now I think it’s a good time to put a conceptual model in place, in order to synthetize what you have read there.

Keyspace = {
    ColumnFamily1 = {
        RowKey1 = {
            ColumnName1 = ColumnValue,
            ColumnNameN = ColumnValue
        },
        RowKeyN = {
            ColumnName1 = ColumnValue,
            ColumnNameN = ColumnValue
        }
    },
    ColumnFamilyN = {
        RowKey1 = {
            ColumnName1 = ColumnValue,
            ColumnNameN = ColumnValue
        },
        RowKeyN = {
            ColumnName1 = ColumnValue,
            ColumnNameN = ColumnValue
        }
    }
}

What does it look like? Did you say a map of maps? Oh yeah, you’re right. Come on walk through each piece now.

Keyspaces

Analogy: Database, schema
There: Twissandra

This is the top-level identifier of our schema. As such, we usually have one by application.

Column Families

Analogy: Table
There:

  • Users
  • Tweets
  • Friends
  • Followers
  • Timeline
  • Userline

Analogously to tables, column families are containers for rows. Each row has a key and contains a number of columns.

Columns

Analogy: Field
There:

  • Users:
    • password = string
    • email = string
  • Tweets:
    • username = string
    • body = string
  • Friends
    • [{user.name} = timestamp]
  • Followers:
    • [{user.name} = timestamp]
  • Timeline:
    • [{timestamp} = {tweet.id}]
  • Userline:
    • [{timestamp} = {tweet.id}]

Columns follow a name/value fashion. As such, their names can be strings, numerics, etc, and are used as indexes, since they are stored orderly. To put it simple, let’s take the Friends column family as an example, whose which each row is keyed by an username.

Friends = {
    'hermes': {
        # friend id: timestamp of when the friendship was added
        'larry': '1267413962580791',
        'curly': '1267413990076949',
        'moe'  : '1267414008133277',
    },
}

Each row has a number of friend usernames as column names (i.e. column names can be used to store values) and timestamps for their values, thus we can easily know which users are following a given user and since when.

This data model breaks away the common relational concept of selecting records through joins among many normalised tables. Here we often design our “tables” with our future data reading in mind, not with the data storing. I mean, what is read together is indeed stored together.

Going to the next page

Super Columns

There is also another type of column, whose is not employed on Twissandra, which is called Super Column. It’s a special type of column that contains a number of regular columns, what let us to something like a map of maps of maps.

Keyspace = {
    ColumnFamily1 = {
        RowKey1 = {
            SuperColumnName1 = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            },
            SuperColumnNameN = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            }
        },
        RowKeyN = {
            SuperColumnName1 = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            },
            SuperColumnNameN = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            }
        }
    },
    ColumnFamilyN = {
        RowKey1 = {
            SuperColumnName1 = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            },
            SuperColumnNameN = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            }
        },
        RowKeyN = {
            SuperColumnName1 = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            },
            SuperColumnNameN = {
                ColumnName1 = ColumnValue,
                ColumnNameN = ColumnValue
            }
        }
    }
}

It might be useful if we decided, for example, that we should have friend details right in the Friends column family, e.g. their long names. In this case, our Friends column family would be actually a super column family like follow.

Friends = {
    'hermes': {
        # friend id: timestamp of when the friendship was added and his/her name
        'larry': {
            'longname': 'Larry Page',
            'since': '1267413962580791'
        },
        'curly': {
            'longname': 'Curly Howard',
            'since': '1267413990076949'
        },
        'moe': {
            'longname': 'Moe',
            'since': '1267414008133277'
        }
    }
}

This model is in line with our aforementioned philosophy of “what is read together is stored together”. And also it’s worth to mention that super column names are stored orderly by name, i.e. they’re indexed, just like regular columns are.

Far more

This blog post was undoubtedly a simplified explanation on the Cassandra data model, relying on an analogy to help people, which might be already familiar with relational data model, to getting started with the Cassandra data model.

So now that you have a basic understanding, I’d strongly suggest you to read the official explanation from Cassandra’s wiki and other good explanations, like these following:

I hope have helped you!

klogd2: My second try to route Syslog messages to Kafka

It was really cool to play around with klogd but I have to confess that I’d like to have more fun. So this is my aim with klogd2.

Klogd2 is essentially a new implementation of klogd but in Java, relying on Syslog4j, as I said on klogd2’s README:

I’d want to try Syslog4j on the server side, because I know it’s a rock solid stuff and all those cool kids are using it, e.g. Graylog2.

Take a couple of minutes to get a look there, when you can. As usual, I’d really appreciate your feedback and possibly a pull request.

klogd: What about route Syslog messages to Kafka?

Today I was searching for a way to route Syslog messages to Kafka, since Syslog is the standard bucket for logs on Unix-like operational systems and there are many legacy applications which use it and cannot be changed to use something else. Unfortunately, I didn’t find anything. Therefore I decided to write something to try it.

Kafka is a pretty interesting high-throughput distributed messaging system from LinkedIn’s Data Team guys, whose aim is to serve as the foundation for LinkedIn’s activity stream and operational data processing pipeline. They have used it to handle lots of real-time data everyday and have open sourced it as an Apache project. I suggest you to take a look on its design and use cases today.

The result of my first try is klogd.

It’s a dumb simple Python program which simply listen for UDP packets on 1514 port and send them to a Kafka server. Just it. So I know, of course, there are many things to be done, because klogd is still too naive. This is just the begining.

Take a time to try it and give me your feedback. Further, fork it, hack it, and send me a pull request.

MessagePack-RPC with Clojure and the AOT compilation

A few of days ago, during a night without sleep, I decided to play a little bit with MessagePack-RPC for Java. It was absolutely a fun time but I ended feeling like it might be better. So I decided to make it more fun and I rewrote those client/server programs in Clojure.

As usual, those codes are on my GitHub account:

Those are two really simple samples, far from those of the Real World, of course, but them still worth enough to try the underlying concept and have fun.

One or two interesting things there

During my experiment, I realized some interesting things about compiling Clojure code ahead of time (AOT) and Java interop that I thought I might share briefly here. It happened due to the nature of the implementation of the MessagePack-RPC for Java library once it does data serialization and deserialization.

Here you will see compilation of Clojure code and what it results. If you want to run the samples, which is worth,you can find instructions in the project’s README.

So let’s look on…

Getting the Clojure project’s code

Once the whole project’s code is on GitHub, this is quite simple task:

$ git clone https://github.com/leandrosilva/msgpackrpc-sample-clojure

Important files in the project:

In this blog post we are going to focus only on server.clj and client.clj.

Inspecting the server

Now you have the code, compile only the server code at first time.

$ lein compile msgpackrpc-sample.server

Ok. So what did the compilation above generated?

$ cd target/classes

$ ls -la

  MathServer.class
  msgpackrpc_sample

The compilation generated:

  • MathServer class
  • And msgpackrpc_sample directory – which is the sanitized name for mesgpackrpc.sample namespace, as you can imagine
Right. This is going to be interesting. Let’s inspect msgpackrpc_sample directory.
$ ls -la msgpackrpc_sample/

  server$_add.class
  server$_div.class
  server$_main.class
  server$_mul.class
  server$_sub.class
  server$loading__4784__auto__.class
  server__init.class

Its content is:

  • A class to load the namespace code
  • A class to initialize the namespace
  • And a class for every function included in the namespace
Really interesting, non? But let’s go further more and inspect MathServer class now.
$ javap -c MathServer

Output:

  public class MathServer extends java.lang.Object {
      public static {};
        Code:
        ...
      public MathServer();
        Code:
        ...
      public java.lang.Object clone();
        Code:
        ...
      public int hashCode();
        Code:
        ...
      public java.lang.String toString();
        Code:
        ...
      public boolean equals(java.lang.Object);
        Code:
        ...
      public int add(int, int);
        Code:
        ...
      public int sub(int, int);
        Code:
        ...
      public int mul(int, int);
        Code:
        ...
      public double div(int, int);
        Code:
        ...
  }

I’m not sure whether you are familiarized with Clojure’s gen-class macro or not but it is the magic that has generated the Java code above.

(gen-class
  :name MathServer
  :methods [[add [int int] int]
            [sub [int int] int]
            [mul [int int] int]
            [div [int int] double]])

Putting it simple, the code above define like a public interface for MathServer class and the code below implements them, as follow.

(defn -add [this a b]
  (+ a b))

(defn -sub [this a b]
  (- a b))

(defn -mul [this a b]
  (* a b))

(defn -div [this a b]
  (/ a b))

This is a pretty much exciting thing, non? This is because if you need to interop your Clojure program/library with any Java program/library (legacy or not) which requires specific interface, you can design it quickly and unpainfuly using gen-class. Further more, you can design it up front, compile AOT, and finally delivery it as any regular Java .class file. Awesome!

Are you curious about the hyphen prefix to every function?

This is the notation to say that a given method implementation should bind to a function with its name plus hyphen prefix. It is possible to define a prefix other than hyphen, which is the default one, using :prefix option of gen-class macro. So if you define :prefix option as “banana-” for MathServer class, every function that implements a method of MathServer class should start with “banana-“.

You can find an example here.

Inspecting the client

The same way you did for server code, you have to do to the client code.

$ lein compile msgpackrpc-sample.client

Ok. Since you already did it before let’s move a little fast here (without verbiage of my end, which is good to you. :)).

$ cd target/classes

$ ls -la

  IMath.class
  MathServer.class
  msgpackrpc_sample

$ ls -la msgpackrpc_sample/

  client$_main.class
  client$loading__4784__auto__.class
  client__init.class
  ifaces$loading__4784__auto__.class
  server$_add.class
  server$_div.class
  server$_main.class
  server$_mul.class
  server$_sub.class
  server$loading__4784__auto__.class
  server__init.class

I’m pretty sure you got it, since it is close to what you saw for server before, but I’d like to just comment a little bit. The compilation generated:

  • IMath class, which is an interface actually
  • Classes for client and ifaces namespaces, pretty much the same happened for server – note that those two are declared in the same file client.clj

So let’s inspect the IMath interface.

$ javap -c IMath

Output:

  public interface IMath {
      public abstract int add(int, int);
      public abstract int sub(int, int);
      public abstract int mul(int, int);
      public abstract double div(int, int);
  }

This is the magic of gen-interface macro!

(gen-interface
  :name IMath
  :methods [[add [int int] int]
            [sub [int int] int]
            [mul [int int] int]
            [div [int int] double]])

And after that “magic”, this interface is imported like any other regular Java class, as follow.

(ns msgpackrpc-sample.client
  (:import IMath)
  (:import [org.msgpack.rpc.loop EventLoop])
  (:import [org.msgpack.rpc Client]))

Wow! I love it, baby! Don’t you?

The End

There are yet other many interesting things on MessagePack, MessagePack-RPC and, of course, Clojure compilation, so I hope you have get interested on them as well, and go learn and try further more, because I definitely will do.

Enjoy it!

A briefly introduction to Cameron

Hi everybody!

I’ve started to write a kind of documentation about Cameron, in the spirit of a presentation, really brief and direct. It’s still a work in progress, like Cameron as well, but I think it’s already able to give you an idea on what Cameron aim to be.

Stay tuned…

A briefly introduction to Cameron

Who is Cameron?

Cameron is an Erlang-based workflow engine in which I have been working on for a few weeks.

It has been built as an Erlang/OTP system with an REST-like API, powered by Misultin, through which one can POST a JSON request to run a given process workflows, that will be executed in background [parallely with other running ones], and then GET its JSON results as soon as them become available. And obviously, it uses Redis for the win.

What about Process Workflows?

Process workflows are defined in terms of REST-like web services, written in virtually any language, which basically must talk a simple JSON contract.

These web services are the activities that define a process workflow; these web services are the tasks to achieve a given target. And as you can imagine, yes, an activity can cascade many others; it is pipeline-based, as well.

So if you have any background job that must cascade many tasks to achieve a goal, maybe it fits to your needs.

Does it work?

Although it still experimental, a work in progress, it works reasonably well — at least under my tests.

So if you have time, take a look at the documentation.