A Software Engineer’s Guide to Large Language Models Software engineer Shane Hobson published a guide explaining how large language models (LLMs) work under the hood, aimed at fellow software engineers. The guide walks through the LLM architecture step by step, from prompt to output, and covers foundational machine learning concepts such as training, forward pass, loss function, and backpropagation. Hobson emphasizes that LLMs are sophisticated software that engineers can understand by leveraging their existing code analysis skills. The introduction of large language models LLMs has caused a tectonic shift in how software is built. When combined with agentic coding harnesses, LLMs can now perform many of the tasks once thought to be the exclusive domain of human software engineers. These tools can write, analyze, and debug code with a level of competency that would shock a time traveler arriving from earlier in this decade. Not a Medium member? Read this free on my blog: https://shanehobson.me/blog/a-software-engineers-guide-to-large-language-models https://shanehobson.me/blog/a-software-engineers-guide-to-large-language-models LLMs empower us to be more productive, remove drudgery, and have greater impact in our work, but they also threaten us — will they eventually replace us entirely? These days, software engineers spend a lot of time thinking about, and using, LLMs. But by and large, most software engineers treat them as a black box — prompt goes in, code comes out. Not many people outside of the machine learning community have taken the time to understand exactly how this technology works. Since LLMs have such a big impact on our work and our lives, I think it is important to try to understand them. This summer I decided to learn about how LLMs work under the hood. In addition to consuming many YouTube tutorials and blog posts from experts in the field, I read the following books: At the end of the day, an LLM is just another piece of software, albeit a quite sophisticated one. I believe that any software engineer who has spent years reading and analyzing code can leverage this experience to learn how LLMs work as well. In this article, I want to provide a starting point: a software engineer’s guide to LLMs. We’re going to walk through each piece of the LLM architecture step by step, starting with the initial prompt and going all the way through to the final text output. By the end, you should have a strong mental model for how these things work. But first, let’s start with an overview of machine learning, the technology that LLMs are built on top of. Machine learning is about creating software that can learn from patterns in data. This is in contrast to traditional software systems, where programs are told how to behave explicitly in code. Machine learning models are able to make predictions about data they haven’t seen before, based on what they learn from data they have been “trained” on. During the training phase, these models are fed data, and they make predictions based on this data. This is the “forward pass” through the system. After the predictions are emitted, they are compared to the actual correct outcome, and a calculation is made to determine how close the prediction is to the actual correct/desired result. This is called the “loss function.” What counts as the “correct” outcome varies depending on the goal of the system. An example of a simpler machine learning system model would be one that accepts a piece of text and determines whether it demonstrates positive or negative sentiment. Here, the “correct” outcome is a binary value: “positive” or “negative.” This is known as a “classification” model. As we’ll see later in this article, the “correct” outcome for an LLM is far more complex: it can be any one value out of the model’s total “vocabulary,” which typically encompasses tens of thousands of values. Next, information about the degree of “loss” is passed back through the model. The model uses this information to make adjustments so that it can make a better prediction the next time it encounters that input, or something similar to it. This is the “backward pass” through the model, also known as backpropagation . How exactly does the model make these adjustments? A machine learning model is made up of “weights,” which you can think of as connections between nodes. If you are familiar with graph theory in computer science, a machine learning model can be visualized as a graph with weighted edges and nodes. These systems are modeled after the human brain, which contains billions of neurons nodes that have weighted connections between each other edges . Sophisticated machine learning models have billions of these connections, and they each have a weight. When a model is first built, each weight is seeded with a random value. As the model undergoes the training process described above, the weights are adjusted, allowing the model to “learn” from the data it has processed and apply that learning when it processes future data. After a machine learning model is trained, it is ready to be used in the real world. A fully trained machine learning model can accept new inputs it has never seen before, and use the information it gained from its training data and recorded in its weights to provide accurate responses to those novel inputs. This final stage of actually using a machine learning model to generate responses to real-world inputs is called “inference.” LLMs are an extension of the machine learning paradigm outlined above. Rather than simply classifying inputs into categories, like our positive-versus-negative sentiment classifier, LLMs can generate entirely new outputs. This is why they fall under the umbrella of “generative AI”: instead of simply placing inputs into predefined categories, they generate new content. A standard classification machine learning model has a predefined set of possible outputs. Our positive-versus-negative sentiment classifier has two: positive and negative. You could imagine another classifier for grouping emails into personal, work, spam, social, etc. This one could have potentially 5–6 possible outputs. In either case, the number of possible outputs is static and predefined, and the machine learning model chooses one of them when it processes a specific input. In LLMs, the set of possible outputs can be thought of as all possible tokens in the model’s vocabulary. I’ll explain exactly what a token is shortly, but for now you can loosely think of tokens as words, parts of words, and symbols. If we forget code for the moment, we could imagine an LLM that only produces English text. This model’s vocabulary would consist of all words in the English language, along with punctuation characters. This amounts to tens of thousands of tokens. Conceptually, you can think of an LLM’s process in the following way: it iterates through the provided input text, and on each iteration, it predicts the next token, appends that token to the input, and then processes the new text with the token appended. It does this repeatedly until the full response is generated. The training for an LLM is quite similar to the training for traditional machine learning models discussed above. In our simple sentiment classifier, the model predicted one of two values, and then used the provided “correct” data to determine if it was correct. There, the “correct” data was a label provided to the model usually by a human of “positive” or “negative.” In LLMs, the “correct” next token is simply the next token in the training data . When training an LLM we start with a full piece of text. Initially, the LLM consumes the first token, and it outputs its prediction of what the next one will be. This prediction is compared with the actual next token in the input text, and then the loss function is run and backpropagation occurs based on that. This continues iteratively for every token in the input. Because the text itself is used as the source of truth, we don’t require human-labelled data to train LLMs. Human feedback is generally required at a later stage of LLM development, but not for initial training. Because of this, the initial training of LLMs is referred to as self-supervised training . By repeatedly learning to predict the next token in a sequence, LLMs learn complex patterns and relationships between tokens, including how their meanings depend on the context in which they appear. This is the key insight that explains how they are so effective at generating text, simply by predicting one token at a time. When training is over and it’s time for inference i.e., real-world use , the model’s weights encode an enormous amount of information about the patterns and relationships it encountered during training, allowing it to apply what it learned to new sequences of tokens. Next, we are going to follow a single input’s path through the full LLM architecture. Here is a high-level roadmap of what we will explore: We will see how the input text is turned into tokens , which are then converted into embeddings . These embeddings are passed through a number of transformer blocks , which contain 1 an attention mechanism and 2 a feed-forward network . Finally, the language model head produces a vector containing a raw score for each token in the model’s vocabulary, and the sampling step uses those scores to select the next token. This process repeats until an end of sequence condition is reached. LLMs rely heavily on linear algebra, which is a branch of mathematics that deals with vectors, matrices, and operations on them. Don’t be scared by the term “linear algebra”; we’re not going to get very math-heavy in this article. But, we do need a high-level understanding of the operations the LLM is performing and the terms used to describe these operations before we proceed. The two important terms we need to understand are vector and matrix . A vector is just an ordered list of numbers, e.g., 2, 5, 1 . It is represented as an array in machine learning code. A matrix is a rectangular grid of numbers, e.g. 1, 2, 3 , 4, 5, 6 . Matrices are represented as multi-dimensional arrays in code. If you’re a software engineer, these will be familiar data structures. A matrix can be thought of as a transformation; it turns one vector into another. The core mathematical operation in an LLM is matrix multiplication , where a vector is multiplied by a matrix to produce a new vector . You can think of this as taking the information represented by the original vector and transforming it into a new representation. LLMs perform these transformations over and over as information moves through the model. These transformations are how LLMs represent the weighted edges of the neural network that we discussed in the machine learning section above. Each value in a weight matrix represents a learned weight in the neural network , determining how strongly one input value contributes to an output value. To multiply two matrices, you multiply every row of the first matrix by every column of the second matrix: There are two more concepts that we will reference below that you need to know: First, dot product . Dot product takes two vectors of the same length and produces a single number by multiplying the corresponding numbers in each vector and then adding those values together. For example: If A = 2, 3, 4 and B = 5, 1, 2 , then the dot product is: 2 x 5 + 3 x 1 + 4 x 2 = 21. Below we will use dot product to create a single number from two vectors. Second is softmax, which is a function that takes a list of numbers of arbitrary value and turns them into values between 0 and 1 that sum to 1. This is useful because it takes different lists of values and normalizes them so they can be compared. For example, softmax would turn the vector 1.2, 1.5, 1.0 into 0.32, 0.43, 0.26 . Below, we will use softmax to normalize the raw output of some operations into values that can be passed into other parts of the system. Everything starts with the input. When you submit a prompt, the agentic harness passes the prompt along, together with the context and a system prompt . The context is all of the previous prompts and responses in the session, along with any data retrieved by the agentic loop. The system prompt gives the LLM meta-instructions such as “You are an expert software engineer. Write clean, maintainable code and explain your solutions concisely.” As responses are generated, new prompts are submitted, and data is retrieved from other sources, all of this information is appended to the context and passed back into the LLM as input. Next, a tokenizer takes the input text and breaks it down into tokens . Tokens are IDs that represent words, parts of words, or symbols. For example, the sentence “Write a function that sorts an array” might be transformed into a token array such as 8144, 264, 734, 430, 29371, 459, 1358, 13 , with the tokens representing the following English language words and punctuation: "Write", "a", "function", "that", "sorts", "an", "array", "." . The model maintains a map of token ID - token and an inverted map of token - token ID for easy transformation. I mentioned above that a token may be a “part” of a word. Tokenizers don’t perform a 1:1 mapping from word - token. Instead, many subparts of words are stored as unique tokens. For example, common word subparts like ing, ed, un, and parts of contractions like 'nt and 's are their own tokens in many tokenizers. This is an optimization that allows the model to work with a smaller vocabulary, which improves efficiency and reduces memory footprint. We’ll see below that models work with vectors that are the length of the total number of tokens in the vocabulary, which is why this optimization is important. There are also special tokens that models may use internally. For example, the