 When I say that this video is probably the most important programming video you're ever going to watch It's partly because what I'm going to tell you is distinctly a minority position among programmers Probably five percent or under programmers will tell you that definitively Objectoring a programming is just not a good idea and in fact is going to lead you astray Maybe you'll have another twenty thirty percent of programmers who will hem and ha and say that it has some virtues and some weaknesses And it might be better applied to some problems than others. I'm not telling you that I'm telling you definitively No object-oriented programming doesn't fit any problem and you shouldn't take it seriously This is almost certainly not what you were told in school if you attended a programming course in the last 15 years Or you read most educational materials about programming the pervasive default assumption is just well object-oriented programming is the right way to go And it's just a settled matter So I reiterate this is probably going to be the most important video you watch about programming because it's going to tell you Something you're not going to get from a vast majority of other sources First off, I'm going to try and make clear exactly what I'm complaining about and what I'm not complaining about And then I'm going to try and explain well What is object-oriented programming really because if we don't nail that down it's almost impossible to criticize and Then I'll try and account for it. Well, if object-oriented programming isn't good. Why does it dominate the industry? That's kind of an important question actually and then I'll actually get into well Why does object-oriented programming not work what's bad about it and then lastly if I'm telling you just to not program in An object-oriented style then what you do instead? What is the alternative? It's called procedural programming, but what does that look like exactly? So what are the problems with object-oriented programming well first off the problem is really not classes per se That is I think it's actually possible to program occasionally with classes in a way that's fairly benign I don't think it's particularly beneficial, but for aesthetic reasons It might seem more pleasing to have an explicit association between certain Functions and certain data types doing this pervasively though as I'll make clear is a really bad idea That's where everything goes wrong is when you try and shove every Function of your code every behavior into an association with the data type that leads to disaster Secondly, I don't think the problem with object-oriented programming is about performance I recommend you watch this talk by Mike Acton called data oriented design in C++ He makes some very interesting points and provides some insight into that world of programming Which most of us don't do but I think he overstates his case fine There's a lot of software out there that should be written with much more regard for performance But I think there's tons of software that just really doesn't apply You'll also hear complaints about excessive abstraction From generally the same people people like Mike Acton and again here. I think they're overstating their case I think abstraction is actually a worthy goal in practice. Most abstractions aren't good It takes a long long time to develop good ones and as I'll explain a major problem with object-oriented programming is it does tend to produce Abstractions that aren't any good. That's the real problem. Not the idea of abstraction itself Another interesting talk to watch is one by Abner Coimbrae called what programming is never about and the thing Which he says programming is never about is code prettiness how code looks aesthetics His main point is that programmers typically focus too much on surface concerns about their code rather than stuff That really matters. I think though he actually simply misstates his case or rather his thesis Doesn't really follow from his arguments, which are generally valid I think when really pressed he would admit that elegance simplicity flexibility readability Maintainability structure all these things you might file under code aesthetics I think he would admit actually do matter But I think the more accurate way to spend his point is that these surface level virtues of code are good things and actually important But object-oriented programming and abstraction heavy programming in general fails to deliver them In fact, it provides just the illusion of these things Object-oriented programming is sold on the basis that it supposedly provides these things, but particularly simplicity and elegance It actually makes things worse Lastly be clear that I'm pushing procedural programming not necessarily functional programming Which is a different thing as I'll make clear in a moment I happen to think that functional programming actually is the future of higher level code I think it may actually be the default way we program at a higher level in 10 years from now or something But there are serious efficiency problems that make functional programming not really viable in certain domains of programming And so my message is whether your code ends up functional or imperative. That's a separate matter Regardless, your code should be procedural rather than object-oriented So it's a good time now to make clear exactly what are the competing paradigms of programming that we're really talking about There are four main possibilities your code can first to be both procedural and imperative procedural meaning that you have no explicit association between your data types and your functions your behaviors and Imperative meaning that we just mutate state whenever we feel like it We don't have any special handling of shared state Which can cause problems as your code gets larger and larger and more complex But in procedural and imperative programming we just cope with the problems as they arise and you can think of this style of Programming as being basically the default. It's the the obvious way to get work done So this is really how all programming was done in the early days of computers But then starting in the 60s as programs got more more complicated people began thinking about well How do we solve this problem of shared state because it really can get out of hand? And so we got two major prescriptions on how to handle the problem One of these prescriptions says that our code should be procedural yet functional Meaning that all or most of the functions that make up our code should be pure They should not deal with state and so programming in the style We would tackle the problem of shared state by minimizing state trying to get rid of as much of it as possible The other prescription people came up with said that our code should be object-oriented and imperative and the strategy here Is that we simply segregate our state we take the state that makes up our program and instead of sharing it promiscuously we try and divide and conquer the problem we package it into these Encapsulated units that we call objects and objects contain other objects and so forth and that's how we conquer the problem and These two prescriptions are actually orthogonal to each other We can do both the functional business to minimize the amount of state Which our program deals with and then whatever state is left over we can then segregate into separate units of encapsulation And in fact I think this combination approach may actually be the ideal way to structure programs At least in terms of high-level code where we don't care so much about efficiency as I'll explain I think segregating state is actually a valid strategy up to a certain level of detail a certain level of complexity And so if we first minimize the amount of state which our code deals with it then becomes a viable strategy to Segregate the remaining state You may have noticed in my definition of object-oriented programming that I said nothing about inheritance And that is because inheritance is simply irrelevant. No one defends it anymore Even people who advocate for object-oriented programming will very very commonly these days tell you to be very very careful in using inheritance Or maybe not to use it at all and so it's not really pertinent to any argument about whether object-oriented programming is good or bad For similar reasons I didn't say anything about polymorphism in my definition because polymorphism really good or bad isn't exclusive to object-oriented programming You can have procedural code that is polymorphic and in fact even more polymorphic than is typically available in most object-oriented languages So it's really not part of the discussion as far as I'm concerned when I complain about object-oriented programming I'm really complaining about one idea encapsulation encapsulation does not work or as I should qualify this Encapsulation does not work at a fine-grain level, which is the core of what object-oriented ideology prescribes That we need to take the state of our programs and divide and conquer that problem by chopping it up into tiny little pieces That is the nature of object-oriented code and it doesn't work. It leads to madness Before delving into why object-oriented programming doesn't work. It is important to address this mystery of well if object-oriented programming isn't so great Why does it now dominate the industry and why has it done so for almost the last 20 years? I've heard it sometimes suggested that well, this was an imposition of management Management wants interchangeable developers so that we can have a cookie cutter assembly line development process Hence business types were really enthusiastic about object-oriented programming promises about code reusability and compartmentalization It's a theory that sounds plausible to me But the main sticking point is that object-oriented programming doesn't actually deliver these promises You'd think people would have noticed sometime in the last 20 years yet. They seem not to have noticed I'm also skeptical of the idea that management actually really inserts themselves in these technical decisions that often I suppose once a object-oriented programming was well established and that became the pervasive norm then yeah Sure management would push towards doing what everyone else is doing so that they can draw from the the larger talent pool But otherwise aside from pushing engineers to just go along with the legacy system and not rebuild everything I just don't think that many business managers really care that much about technical decisions I'm much more inclined to think that object-oriented programming is something that programmers did to themselves and the question is then well Why I think a big part of the answer simply comes down to Java When it was first introduced in the mid 90s Java seemed like a welcome reprieve to many programmers compared to the alternatives Java seemed really simple For example on the PC. This is what application development looked like you had to use the Win32 API in C And not only did programmers have to concern themselves with memory management as you do in C But on top of that Win32 just doesn't feel like the C that you would learn from books It's not what you would learn from K&R. It's not what you would learn in school It's all this excess macro heavy stuff on top that is really mystifying Even the tools you would use to write C programs on a Windows platform the visual studio tools You know wouldn't be the same as what you would learn in university probably where you probably had a Unix system And that's what you learned So it was already this platform with a quite high barrier to entry But then also in this period it was undergoing this ugly transition from Win16 to Win32 And so you can begin to see why programmers were desperately looking for some way out The only real alternative at the time in PC programming was what visual basic But that was another effective Microsoft platform You're locking yourself into and I suppose otherwise you might use Pascal or Delphi But that platform had its own issues And so it shouldn't be too surprising that when Sun Microsystems came along and said here's this free thing that Everyone can use across all platforms that got people's attention And Java had other things going for it certainly seemed more accessible just in terms of like its naming conventions For example, you look at the Java API's and you see things like file input stream, which is not cryptic at all Yes, there are definitely issues in how abstracted many of the API's are and you know having to derive base classes to use the API's and all that Nonsense, but on first glance on surface inspection. It certainly seems like a friendlier system It's not like Unix where you have stuff like IOC TL Which is you're supposed to know is input output control and other really horrible abbreviations and then Win32 had the same thing, you know LPCTSTR stands for Was long pointer to a const tchar string So even if you know what a tchar is and a long pointer is you're stuck in this world where everything is cryptically Abbreviated and it's just this god-banned puzzle that you have to figure out at every step Java came in and said no we don't necessarily have to program that way We can write real programs that don't have to be horribly cryptic in that way and then Java took things too far in the other Direction, but that's again. We'll get to that Java also smartly had the C like syntax the curly brace syntax So superficially at least it seemed familiar to programmers from C and C++ and it seemed like real programming It has curly braces after all and then the whole compilation to VM bytecode business was again very alluring to programmers trying to escape their Platform headaches and then Java also offered some very basic niceties like proper namespaces without header files For Christ's sake, we still have to deal with header files to do our real programming in C and C++ At least 20 years after we should have ditched them if for this one thing alone I think it's worth giving Java some credit at mainstreamed programming without header files And then of course also very alluring garbage collection I know some hardcore low-level programmers out there will insist that garbage collection is never necessary It's never a good idea, but whether or not that's the case. It's really hard to argue with the appeal It shouldn't be surprising that the vast armies of people doing business code applications wanted to stop thinking about memory management Java also mainstreamed exceptions as the primary way to handle errors and whatever problems this may have in practice I think it definitely seems appealing because the alternative is ugly The alternative is what we do in C and C++ of having to have an inbound error return value or like you know Saving to a global and checking the global after every time you call. It's not pretty Go lying with multiple return and that style probably is the better way to go But that's not the solution Java came up with and so it normalized this other thing that seemed better at the time. I Think some people also came to like the subject verb object nature of method calls over straight function calls Because well, this is just what we do in English. It's subject first then verb then object I myself don't find it all that appealing I prefer consistency and I think the distinction between subject and object in many many cases gets very very murky Which is one of the problems with object-oriented programming as we'll get to but the style of syntax in Java led to this convenience people I think since then have become addicted to which is in their IDEs it offers them for this data type. What are my options? What can I do with this thing? It seems to enable a style of programming where you can just sort of browse You don't have to hold all the options in your head. You just have a vague notion of way I'm gonna take that thing and transform into this other thing I don't remember exactly what the methods called. I'll just grope my way there using auto completion in my IDE Again, there's really actually no reason you couldn't have the same style of convenience in a purely procedural language You just have an auto completion for given this first argument What functions take this type as its first argument and it be effectively the same thing really But because of quirks of history and syntax design, its particular editing convenience has been implemented for languages like Java But generally not straight procedural languages And I think method auto completion may actually largely explain why people sometimes claim that object-oriented API's Feel easier to use. It's because you can largely auto-complete your way through most of the usage Another thing Java seemed to have going for it is that back in the mid 90s This was the heyday of GUI programming and it seemed really logical to map components as we see them in a GUI window and Classes in an object-oriented program that seemed like a very natural correspondence This was the most tangible version of the real-world modeling which object-oriented promised At the time it seemed like a very plausible story and on top of that you have the virtue of Java being supposedly Cross-platform with the Java swing API, so you can write GUIs that will run on any system They'll look horribly ugly, but at least hey, they run on everything You could do so-called rad rapid application development of GUI applications like you do in visual basic Except in Java, you're not locked into Microsoft's platform So the funny thing to me about Java is that I think in an ultimate history It could have had virtually all the same success if not even more perhaps if it weren't object-oriented at all It could have just been a straight procedural language and would have had still a big long list of attractive selling points We could have had all the same portability the same garbage collection the same exception handling and so forth down the line Without any of the object-orientedness or at the very least without forcing everything into the mold of classes You could have a language like Python say where there are classes But also just straight procedural code if you want and they can live side by side just fine So there still is this question Java aside there seems to be some appeal to object-oriented programming in itself and what is that? Well, I think very simply if you go back to the 60s and 70s as people were grappling with the problems of software systems getting larger and larger People tried to identify units of code abstraction that were larger than individual functions and data types It's natural to want to describe any complex system in terms of large-scale components You know if you talk about human anatomy that you don't explain it first in terms of microbiology That would be nuts. We first talk about very major organs like the brain and the heart and the kidneys and so forth as Software gets larger and larger it felt like these units of code We were building out of the base materials these data structures and functions. They became smaller and smaller relative to the whole Sadly though the one general answer people could come up with of what is a unit of code abstraction bigger than a function and Bigger than a data type is just simply a combination of the two and hence objects were born We took our functions and our data types and we associated them together into these larger units We want to think in terms of paragraphs rather than individual sentences and object-oriented programming seemed to have an answer for how we could do that It's also very natural that as we build larger and larger systems and complex things as much as possible We want simple rote rules to guide us object-oriented programming seemed to present a unit of abstraction and a set of guidelines Whereby we could incrementally accrete larger and larger systems This line of thinking is what led us to patterns and then the so-called solid principles and dependency injection and test driven development And all this stuff which has subsequently been piled on by many people who insist that this is now the one true way to do object-oriented programming But to me all these best practices represent band-aids They are compensation for the fact that the original vision of object-oriented programming has never panned out and every few years There's a new ideology in town about how we actually do object-oriented programming for reels this time It's very easy to miss this dynamic I know I did for several years because I think within all of these Addendums to object-oriented programming that there's lots of mystical speech dancing around genuine insights, but it's not quite cohesive Object-oriented programming feels like this circle, which we've been trying to square for over a generation now Finally, let's talk about what's really wrong with object-oriented programming specifically encapsulation, which is the lintspin of the whole thing So consider what is an object an object is this bundle of encapsulated state And we don't interact with the state of that object directly all interactions with that state from the outside world coming through Messages messages to the object the object has a defined set of messages Which it will receive called its public interface and so we have private information hidden behind the public interface When an object receives a message it may in turn send messages to other objects And so we can conceive of an object-oriented program being this graph of objects all communicating with each other by sending messages Many people today forget though that the original conception of a message is not exactly synonymous with just a method call Yes in practice it means calling methods, but a message strictly speaking sends only copies of state It doesn't send references a message sends and returns information about state not state itself And well wait a minute objects themselves are state and this has some interesting consequences It means that strictly speaking messages cannot pass around object references I've never seen a Java or C sharp code base that ever follows this rule Perhaps some small talk programs have but in general this rule is not observed at all and probably for good reason as we'll discuss But anyway if we take the rule seriously It means then for an object to send a message to another object the first object must hold a private reference to that other Object because otherwise how is it going to talk to it to talk to an object? You have to have a reference to it and where is an object going to get a reference to another object if it can't get object references from messages The references which an object needs have to all be there at the object's inception They have to be there for the whole lifetime of the object and There's an even deeper consequence Which is that if an object is sending messages to another that other object is part of the first object's private state and by the principle of Encapsulation an object should be responsible for all the objects which it sends messages to This should be obvious if you consider that messages indirectly read and modify state when B sends a message to a here It's messing with the state of a indirectly sure, but it's still messing with its state And so what happens when other objects come along and send messages to that same object? What's happening here? We have shared state It's hardly any different than if you had a single global variable being shared by say ten functions If you have an object receiving messages from ten other objects those objects are all effectively tied together because they're implicitly sharing this state Sure They interact with that state are indirect through public methods But those methods are providing very trivial kinds of coordination of the state You can impose rules through the accessor methods like saying oh if you access this field It's a number while you can only increment that number you can't mutate it in any other way fine But it's a very trivial kind of protection the hard problems of shared state are much much deeper Where in the system of ten objects all sharing the state is the real coordination? And the answer is there isn't any as soon as you have objects being shared encapsulation just flies out the window So if we're taking encapsulation seriously the only real way to structure a program to structure our objects as a graph is Not as a free-form graph, but as a strict hierarchy At the top of our hierarchy We have an object representing effectively the whole program It's our God object and that has its direct children which represent the subcomponents and those children in turn have their own Subcomponents and so on down the line and each object in the hierarchy is responsible for its direct children And the message is being passed strictly only ever go from parent to their direct child The God object here for example is not supposed to reach down to its grandchild It has to do all of its interactions with this grandchild indirectly through the grandchild's parent Otherwise who really is responsible for that object? Who's managing its state? It's supposed to be the direct parent And so what happens when we have some sort of cross-cutting concern like down on the hierarchy? It turns out oh wait There's some business that that object has with another object in a totally different branch of the hierarchy How do they talk to each other? Well not directly everything has to go through their common ancestor For a to send the message to be here It can't actually directly invoke any kind of method it has to mutate its own state in some way and then Information about that state that new intention of the object gets returned from a message sent from a's parent A's parent in turn same thing has to happen So it gets back up to the common ancestor and then only finally when we get to the common ancestor can that intent be realized as A series of message calls, but not directly down to be it has to be bucket-brigated down through the hierarchy That is how you handle cross-cutting concerns in a strict encapsulated hierarchy Obviously no one writes programs this way or at least no one writes whole programs this way and for good reason It's an absurd way to have to write your code Now you might argue that people do follow these principles in practice They just do so inconsistently and perhaps there is some value in a code base where you apply these principles Inconsistently perhaps half-ass encapsulation actually gets us something So imagine we have some sort of freeform graph of objects making up a program and we decided oh well Is this subsystem of objects that together should be their own self contained encapsulated hierarchy of objects? And so we're going to refactor our code Well very often what that means is not only do we have to do a lot of complicated Rethinking of the structure of the relationships here of what calls what on the other objects We very typically have to introduce more objects like say here to represent this whole new subsystem We probably have to introduce some new sub-god object some ruler of this subsystem Now all interactions with the subsystem have to be reconceptualized as going through this minor deity So say we successfully do this refactoring and now while our code doesn't follow the principles of encapsulation perfectly It's doing so in a half consistent way and maybe there's some benefit there Well, I think what tends to happen is subsequently we decide oh wait we need some new interaction between elements of this encapsulated subsystem and Instead of having to do the hard work of figuring out how exactly it all gets coordinated from the roots of that subsystem The temptation is to just handle the business directly But if we want to do the proper thing we have two options It maybe it turns out that that stuff external to the subsystem actually just needs to get integrated into that subsystem And so it comes under the purview of the subsystems route But otherwise we now have two subsystems that need to coordinate and who's going to do the coordination Well now we need a new subsystem god object responsible for the collective business of these two subsystems and now all interactions of these two Subsystems have to go through this route objects But also all interactions with the outside world and these two subsystems have to go through this new route object So as you can see chances are really good that what you would actually do is say fuck it and just do this You would just reach in and have the objects directly interact with each other whether they should properly do so or not and now Where is the encapsulation? What's the point? Whether you follow the rules strictly or loosely you're in a bad place If you follow the rule strictly most things you do end up being very unobviously Structured and very indirect and the number of defined entities in your code base proliferates with no end in sight The nature of these entities tends to be very abstract and nebulous, but alternatively if you follow the rules loosely What are you even getting? Why are you bothering? What is the point? When I look at your object oriented code base what I'm going to encounter is either this over engineered giant tower of abstractions Or I'm going to be looking at this inconsistently architected pile of objects that are all probably tangled together like Christmas lights You'll have all these objects giving you a warm fuzzy feeling of encapsulation But you're not going to have any real encapsulation of any significance What people tend to create when they design object oriented programs are overly architected buildings where the walls have been Prematurely erected before we have really figured out what the needs of the floor plan are and so what happens is down the line Turns out oh wait, we need to get from this room over here to that room over there But oh wait we've erected barriers in between so we end up busting a bunch of holes through all the walls like the Kool-Aid guy And the resulting pattern is really not organized at all. It's just Swiss cheese We thought we were being disciplined neatly modularizing all the state But then the requirements changed or we just didn't anticipate certain details of the implementation and we end up with a mess The lesson we should take from this is to be very careful about erecting barriers about imposing structure It's actually better to start out with a freeform absence of structure rather than impose a structure that will likely turn out to not really fit Our problem bad structure that doesn't really fit our problem Not only makes it harder to implement the code in the first place it hinders change And it confuses anyone who looks at our code because it's implying one thing But then what's really going on is another in the object oriented world We have to think about all these graphs. We have to think about an inheritance hierarchy We have to think about a composition graph. We have to think about data flows between the object and also we're thinking about a call graph The liberating thing about procedural code is there's just the call graph We also of course do have to think about how our data is structured and how our data gets transformed throughout the course of the program But the beauty of procedural code is that we can think about that totally independent of any notion of responsibilities When I'm looking at my data, I can think just about my data and when I'm looking at my functions I'm not thinking about all these self-imposed barriers I'm not constantly trying to group and modularize everything into these small units of so-called single responsibilities When I sit down to write object oriented code, I always have to play this game I have this mental list of the obvious data types Which my code will deal with and to have this separate mental list of all the imagined behaviors I want in my program all the functionality I imagine it to have and then what object oriented ideology demands is that I take all my behaviors And I somehow associate each one with one of my data types Inevitably what this means in any non-trivial program is I'm actually going to have to introduce all sorts of additional data types Just to be these containers for certain behaviors, which otherwise don't naturally fit with any of my obvious data types The data types I knew I actually wanted because they represent actual data I need in fact as programs get larger and larger in object oriented code It tends to be that these unobvious unnatural data types tend to actually predominate You end up with a majority of so-called data types, which really aren't there because they're representing data They exist simply as a tax to conform to this ideology about code modularization Very quickly we end up in what Steve Yege called the Kingdom of Nouns Where every aspect of our program has to be reconceptualized as not just mere standalone verbs, you know, functions They have to be reconceptualized as nouns, things that represent a set of behaviors And so what we get in our object oriented code bases are all these service classes and manager classes And other what I call doer classes, these very nebulous and abstract entities Even when dealing with data types and behaviors that are relatively concrete Which have fairly visible connections to the functionality apparent to actual users of the program Even here the matchmaking game constantly presents us with these obnoxious philosophical dilemmas In object oriented analysis and design we constantly have to ask ourselves stupid questions like Should a message send itself because maybe instead we should have some sender object which sends messages Or wait a minute. Maybe there should be a receiver object which receives messages or a connection object which transmits messages So very quickly the real world modeling which object oriented programming promises becomes a fool's game Where there aren't any real good answers In my experience object oriented analysis and design very quickly becomes analysis paralysis If you take the ideology seriously as I did you're going to waste a lot of time Hemming and hawing about to conceptualize these elements of your program Object oriented programming is generally sold to students on the basis of these trivial examples that neatly model real world taxonomy But what we get in practice from object oriented analysis and design is a lot of very abstract Excess structure with no obvious real world analogs Note here that programmers have their own peculiar definition of abstract When programmers talk about abstraction, they're generally talking about simplified interface over complex inner workings What's odd about this is that in more general usage abstract as a connotation of being hard to understand Something which is abstract has no resemblance to the things of common daily life And it turns out that most things which programs do are abstract in this sense And so it shouldn't be surprising that we have great difficulty Conceptualizing the components of a typical program in terms of neatly self-contained modules particularly modules which have any real world analog When we pollute our code with generic entities like managers and factories and services We're not really making anything easier to understand We're just putting a happy face on the underlying abstract business and for every excess layer of abstraction We're getting more abstractness In attempting to neatly modularize and label every little fiddly bit that our program does We're actually just making our program harder to understand Something that happens all the time when I look at object-oriented code bases Is that I'll try and find the parts in code that correspond to some user-visible functionality But trying to find the functionality going by clues from the names of classes and the names of methods tends to be very misleading Very typically my expectation that functionality x would be in the class named x turns out to be wrong Because the abstract nature of what we typically do in programs generally necessitates that functionality is not going to be self-contained It's not going to neatly fit into one neat module And so the class which is called x will very superficially relate to x But then all the real work is done elsewhere scattered throughout the code This makes me question. What is the value of having a class called x if it doesn't really contain all the business of x What this class x really represents is actually misleading code structure And how is that helpful? How is that conducive to understanding of your code base? The other reason I have this problem reading code bases and trying to track down where functionality actually lives Is because object-oriented design tends to fracture functionality in our code It tends to take what otherwise could be relatively self-contained code And split it up into many separate methods across many separate classes typically often in many separate files for god's sake This fracturing is accepted because of an ideology about encapsulation and this notion of classes and methods Properly having so-called single responsibilities And there are certainly valid arguments for that idea Certainly it is much easier to get a small short function correct than to get a large sprawling function correct But the important question is that in splitting our code up into many little small methods and to many separate classes Are we actually decreasing the total complexity of our program or just displacing the complexity Just merely spreading it around In either case there's this attendant trade-off we're making Where by splitting up larger units of code into many smaller ones We're greatly increasing the so-called surface area of our code Where I come along and I look at your code base and I try and get a foothold And everything split up into these tiny little units these tiny little packets of code Reading this kind of code often feels frustrating in the same way It can be frustrating to eat a bunch of little candies that are all individually wrapped And when all your methods are really really short You end up having to jump all around the code to find any line of logic A lot of business that otherwise could be neatly sequentially expressed in longer methods gets artificially split up So it feels like you've taken a neatly sorted deck of cards and thrown them into the air so you can play 52 card pickup Okay, so if you're not going to be writing object-oriented code, what are you going to be doing instead? You're going to be writing procedural code. But what does that look like? Well, as I mentioned at the beginning, this doesn't necessarily mean you need to avoid classes entirely If you have a language like python or c++ where you have both straight functions and also classes There are some cases where the association between your data types and certain functions is really really strong That it fits some organizational purposes to just explicitly associate them together by making those functions methods of that type The most obvious example would be adts abstract data types things like cues and lists and so forth The key thing to keep in mind however is that the moment you start hemming and hauling about whether this particular function Really has a primary association with that data type That should be the moment you say screw it will make it just a plain function Because it turns out that most things we do in code tend to be cross-cutting concerns They don't have necessarily special obvious relationships with particular data types They might concern more than one data type and that's why you should only prefer functions So you don't have to play this silly game of matchmaking functions to data types So we're going to be writing our code primarily out of plain functions And we're not going to attempt to encapsulate the state of our program at a fine-grain level because it doesn't work However shared state is still a problem. And if we're not careful it can get out of hand We can't totally solve the problem unless we do pure functional programming But short of that there are broad guidelines we can follow to mitigate the problem First off when in doubt parameterize This means that rather than passing data to functions through global variables You should instead make that data an explicit parameter of the function So it has to get explicitly passed in as much as possible. We want data access in our program to flow through the call graph So anytime you're tempted to pass data to a function through a global because it seems more efficient or maybe just more convenient You should give that a strong reconsideration Secondly, whatever globals you do end up within your program It can be slightly helpful to group them logically into data types Even if this means you effectively have a data type with one instance in your whole program This little trick can often make your code seem just a little bit more organized In a sense, you're just using data types this way to create tiny little sub namespaces But if you do a good job logically grouping your globals this way as a side benefit This can complement rule number one because now you can more conveniently pass this global state to functions By bundling your data together into types you typically cut down on the number of parameters Which functions have to take though do be careful. There is an art to how you logically group things together The third guideline is to opportunistically favor pure functions Even if you're not explicitly working in a functional style or working in a functional language If you see an opportunity to make a function pure it's generally good strategy to take that opportunity Again, pure functions tend to come in at efficiency cost But the brilliant thing about pure functions is that they're the only truly self-contained unit of code When I'm reading and writing a pure function, I don't have to think about anything else I can just consider that function entirely unto itself Therefore they tend to be easier to understand and to make correct The fourth guideline is that we actually should try to encapsulate our code Only in a very loose general sense at the level of namespaces packages modules, whatever your language has So when I'm working in golang, for example, I think of each package as having its own private state and then a public interface I find that encapsulation at this coarse grain level tends to work because you're typically dealing with much larger units of code Then they supposedly ideal classes of object-oriented programming The typical golang program is going to have not that many packages Maybe like tan at the high end and structuring a mere handful of elements into hierarchy of encapsulation Is reasonably doable When it turns out during development that oh wait, I have some cross-cutting concern of my packages And so we're going to need to violate this perfect hierarchy of encapsulation Again, it's not such a big deal because you're dealing with a relatively small graph of objects All the basic problems of encapsulation are still there It's just that the coarse-grained macro level the problems are reasonably manageable The last guideline is that you shouldn't be scared of long functions For a long time now programming students have been advised to when in doubt chop their code into smaller and smaller functions But doing this has significant costs. There are trade-offs It turns out that most programs have these key sections where most of what the code is doing is a long laundry list of stuff And what we're told to do in these scenarios is write functions like this where all the business has been extracted out to separate functions The problem with doing this pervasively is that what was Naturally a logical sequence of code and was otherwise written in sequence top to bottom is now spread and out of order throughout your code base Obviously in cases where the business extracted to a separate function is something that you want to call in multiple places That's a very good reason to have a function But if all these functions were just called in this one place I would generally prefer looking at code where the business of those functions is just done in line And if we want high level documentation of what's going on in my funk here Then you just put what I would call a section comment denoting what each section of the code does In this arrangement the sequence of the business is totally clear And when I'm browsing the whole code base when I'm looking outside this function There's less clutter because there are now fewer functions I have to look at and wonder well. Hey, where is that called? I wonder what that thing does It also has the advantage of letting us avoid having to name functions Naming stuff is really important in code, but it's really really hard to do well And in general I find it preferable if we can avoid naming entities as much as possible In this arrangement, we don't have to think hard about what to call these functions We can just have a comment line and have a full english sentence Which generally is better at conveying accurate meaning and also is simply easier to write If for whatever reason it doesn't seem adequate to simply comment a section rather than extract it to a separate function The next best thing is to make it a private function a nested function such that it's clear This function is not called anywhere else. It's only called within this function In this arrangement I as a reader of your code coming from the outside I'm still presented with a smaller surface area fewer entities in the code And so it's just easier for me to get a foothold Now when you do write functions, which are hundreds if not thousands of lines long You still should keep in mind general guidelines about code readability basic things like not strain too far from the left margin For too far or for too long, you know You don't want to have code that's indented in eight levels because it gets really obnoxious scrolling them down code If you have to scroll over if one thing and also it tends to just imply there's a lot of busy logic in this part of code And it gets confusing So likewise, you also need to look out for parts of functions where the logic is just getting too complex The first thing to do is of course to try and simplify your logic But feeling that there are going to be cases where hey, we should just split this off into a separate function So it's more neatly self contained complexity The other concern with longer functions is that as your function gets longer and longer You tend to accrue more and more local variables So what you want to do is hopefully your language allows this You want to try and constrain the scope of the local variables So that they don't exist for the full duration of the function but rather for subsections This way either reader of your code when they scan up and down the function I don't have to think about all the variables for the whole duration of the function The way this is done in most curly brace languages is you can just introduce a new sub scope with curly braces So here for example, this integer x variable only exists within these curly braces When you have subsections of a function which you are commenting It's generally a good idea to win in doubt and close them in curly braces This gives readers of the function an assurance that variables from the preceding sections don't fall through to the following sections And so in later sections, we don't have to think about the variables that were used above Where possible the even better thing to do is to enclose these local scopes in their own anonymous function That's then just immediately cold And the advantage here within the nested function is that it's not just its own sub scope But also within this anonymous function, it's guaranteed that any return is not going to return out of the enclosing function It'll return just out of this enclosed function And so we have a stronger guarantee that the logic of this subsection is self-contained from the enclosing function Unfortunately, what I often really want when creating subsections of longer functions is a feature that doesn't exist in any language I know of it's an idea. I've only seen in one other place It was Jonathan Blow and his talks about his programming language that he's making And the idea is that we want something like an anonymous function Which doesn't see anything of its enclosing scope The virtue of extracting a section of code out to a truly severed function Is that everything that comes into the function has to be explicitly passed to a parameter It would be great if we could write inline anonymous functions with this same virtue Specifically what I would propose is imagine we had a reserved word use that introduces a block And in the header of the use we list variables from the enclosing scope Which we want to be accessible in this block But otherwise anything from the enclosing scope would not be visible These listed variables however would really actually be copies of those variables So if you assign to x or y here in this scope you're assigning to x and y the local variables of this use block Not to x and y of the enclosing scope Which is the effect you get with a truly separate function, right? You assign to the parameters of a function You're not modifying what was passed to the function. You're just modifying those local variables That's the same thing we want in this use block Furthermore a use block should itself return values So you use return inside the use block and it doesn't return from the enclosing function It returns from the use itself the use is an expression and so we can return values from this use and assign it to this variable a So in effect, we'd have this block of code which is as neatly self-contained as a separate function However, it is written in line And so it's very very clear that oh this is a piece of code that's only used in this one place You don't have to go look for it elsewhere And also we don't even have to give it a name instead We can just put a section comment header before the use block And that is generally much better for containing the actual intent of this block of code If later down the line we decide that this block of code actually should be extracted to its own proper function That's a very easy thing to do. You can have an editor convenience that does that for you automatically It's already clear what the parameters and the arguments should be All the programmer would have to do is provide a name for the new function So anyway, it'd be nice if languages had this feature Unfortunately, I don't know of anyone that does but regardless you shouldn't be so scared of long functions They actually have their place in most code bases At the very least, I hope I can get you to try procedural programming It doesn't really matter what language you're in if you're in java or c-sharp You can write procedural code. You can break the rules But if you've ever felt any of the paralysis that I've felt attempting to do object-oriented programming properly to square the circle I think you'll find abandoning all those ideas and just reverting to procedural code to be a liberating experience I can tell you from personal experience of having read these books that you don't need to read them They don't have answers. They're not going to square the circle and you're going to waste productive years of your life trying to live up to their ideals I'm not saying there's nothing to these ideas. There are bits and pieces that have value Test driven development, for example, has some interesting ideas. There's value in testing But that's part of the problem is that kernels of good ideas have been taken to holistic extremes In a way that I think has been disastrous for the industry and certainly for programming education There are very few solid holistic answers about how we should write code We'd all be better off if we stopped chasing that chimera