Peanut Butter is Real Programming
Steven Lipton

Peanut Butter is Real Programming

If you think programming is a job like any other, I’d like to offer you a peanut butter sandwich. I’m not going to give you one, but something as seemingly simple as a peanut butter and jelly sandwich when looked at from the view of an IT project may blow your mind. Application development and Information Technology is a mindset more than a skill. I've never come up with a good way to describe what I do and the challenges of this profession before, until very recently.

In the comments for another article I wrote, someone referred me to a extremely funny YouTube video about exactly this, though it would not seem it at first. 

If you don’t have time to watch, the story is this: a dad asks his the kids to write the directions for a peanut butter and jelly sandwich. For each set of instructions he gets, the dad explicitly follows their directions. Hilarity and failure ensues as the kids see what happens to their directions, which fail to be explicit. According to the comments on the video this is an exercise from a Harvard Computer Science class, but it intrigued me how far the analogy worked beyond coding a sandwich. 

Coding

If you tried to do the exercise of writing down a peanut butter sandwich like the kids you'd might start like this:

Get bread bag

Open bread bag 

For 2 times

    Remove bread slice

    If plate has a bread slice,
        move 1 bread width next to current bread

Place bread slice on plate

Get peanut butter jar

Open peanut butter jar. 

Get Knife

Grab from handle

Move to work area

Spread Peanut butter 

Place knife blade down into open peanut butter jar

Move knife forward and up

Remove knife from jar

Move knife over bread

Rotate knife with peanut butter side down

Put knife to bread.

Move on bread 

Remove knife from bread. 

I'll stop there. You'd then go through the process of getting the jelly on and then placing the two pieces of bread together, but you get the idea. If you’ve watched the video, you know that the steps above will fail. You'd need to to be extremely detailed to get a workable solution, and this still isn’t detailed enough. Of course that’s what a coder or programmer would need to do -- make the most explicit description possible. Any ambiguity will lead to failure - Almost. 

That’s the coding everyone talks about. You must be very explicit. Not only do you need to be explicit, you need to use the proper language to describe things. For the sandwich we have English to describe the process, which has too many ways to describe the ingredients and tools to be useful. In application development these are programming languages. Different environments use different languages, but all limit descriptions to useful information. Sometimes a choice of language is a matter of preference, sometimes platforms require a language. They can look and act very different from one another for the same action. For example, these four code snippets compute the number 8 and place it in storage, in five different languages. 

const int a = (2+4)-7+3*3; 



SELECT (2+4)-7+(3*3) as ‘a’  



let a = (2+4)-7+(3*3)



2 4 + 7 - 3 dup * + 

Even when we want them to have the same meaning, just as in natural languages, they cannot be identical. The last snippet can change values, while the first and second will remain constants. The third is actually two languages. Depending on the language, the result is different. In Swift the result is a constant, in BASIC a variable. 

Knowing only one of these gives you only one way of looking at the computations. Knowing all five gets deeper into what is going on with the computations, at very different speeds. Knowing all five gives you the power to work on several different platforms and write a home controller with C++ in the the first or create a Profit and Loss statement with SQL in the second. With the third, your can write an Excel Macro as BASIC or an iPhone app in Swift. The fourth, which is FORTH, controls Boeing 777 aircraft wings and the Cassini space probe. While each could be used for the other’s application, a language works best as the tool for its own job. Most developers know more than one language, and know the differences in coding each. 

All of these languages require explicit instructions. They also remove us from many of the tiny mundane operations to get there. We can imagine a language which works best with food and food functions that makes the sandwich easier to make. It has a series of functions that are commands in English

let      get      spread       cut

open     heat     cool         add

layer    mix      putBack      close

flip     squeeze  pour     

There may be more, but we’ll stick with those for now. I’ll also have a few words to modify those functions and some math operators: 

from      with      as      on    

by        if        else    begin  

end       +         /        -      

*         constructor       object

I can write my sandwich like this: 

get breadBag from Cupboard as BreadBag with kind white
get peanutButterJar from Cupboard as PBJar with brand dippy
open peanutButterJar
open breadBag
get breadSlice1 from breadBag as BreadSlice
layer breadSlice1 on Counter
get breadSlice2 from breadBag as BreadSlice
layer breadSlice2 on Counter
get knife from drawer as Knife
get peanutButter from peanutButterJar with knife
spread peanutButter on breadSlice1 with knife
clean knife
get jelly from Cupboard as JellyTube with flavor strawberry
open jelly
squeeze jelly on breadSlice2
layer breadSlice2 on breadSlice1 by flip

This code describes making a sandwich. All of the details of how to put a knife in a jar correctly and get our peanut butter are no longer our problem: the languages has a set of instructions on how to deal with that. Most computer languages are like this code above. The details are hidden from the developer, letting the developers get to the problem they are trying to solve. 

One way of hiding those details is to create objects. BreadBag, BreadSlice, Cupboard, Knife, PBJar and JellyTube are all objects. Each object has its own way of doing things hidden in the code for the object, often referred to as methods. Each object also has properties, the bread has a kind, the Peanut butter jar has a brand and the jelly a flavor

Notice I got the bread and peanut butter first and the jelly much later. I could have gotten all the ingredients at the same time and gotten them ready. This brings up an important part of programming: there is never one solution. Programmer preferences, user requirements, efficiency in code, scalability and style might make decisions that give the same results, but with different code. 

Sometimes, those decisions are user or management mandated, such as cutting a sandwich into triangles or rectangles. You may have someone who only eats a sandwich cut into triangles. This is why program requirements are so important in a planning stage. It informs the programmers the best way to do something. 

Hacks

Speaking of cutting, we have a problem. How do you cut the sandwich in half? In the code above I can’t do this

cut pbSandwich with knife at pbSandwich with width / 2

because I have no object named pbSandwich. I can't just cut one piece of bread, or can I? I could try

cut breadSlice2 with knife at breadSlice2 with width / 2 

Won’t cut my sandwich completely, but this might. 

cut breadSlice1 with knife at breadSlice1 with width / 2

Because the breadSlice1 is the bottom slice, the knife has to go through the top slice, cutting both. 

This is an example of a hack. While the word hack has gained popularity as something good and constructive, they are very, very bad things in programming. Hacks are shortcuts taken in code that break the rules of style, make it difficult to communicate the code, and very often rely on system functions that may not always be true. 

The knife object ignores it is cutting the second bread slice, but it could also complain with a fatal error, the computer equivalent of a temper tantrum. It could complain that the breadSlice2 is in the way and it wont cut.

If I told you to cut the bottom slice, wouldn't you have a bit of a brain lock? That’s the problem with hacks. They are extremely unreliable in every way, and should be avoided. However they do make it into code, and that’s been known to cause lots of problems. 

User Functions and Objects

If we don’t use the hack, what could we do? We could make a new object called PBSandwich, then cut a PBSandwich. A PBSandwich would have a special method called a constructor which would make the sandwich. This object would also have properties: the kind of bread we use, the jelly flavor and the peanut butter brand. I can define the object and the properties in the object like this: 

object PBSandwich begin

  let jellyFlavor be strawberry as JellyFlavor

  let pbBrand be dippy as PBBrand

  let breadKind be white as BreadKind

object end

I used default values for these properties, and told the system that they were objects of JellyFlavor, PBBrand and BreadKind. Any sandwich made with the PBSandwich object will be a Sandwich on white bread with dippy peanut butter and strawberry jelly. 

I add the constructor to the object to make the sandwich, giving us our final code

object PBSandwich begin

  let jellyFlavor be strawberry as JellyFlavor

  let pbBrand be dippy as PBBrand

  let breadKind be white as BreadKind

   

  constructor begin

    get breadBag from Cupboard as BreadBag with kind breadKind

    get peanutButterJar from Cupboard as PBJar with brand pbBrand

    open peanutButterJar

    open breadBag

    get breadSlice1 from breadBag as BreadSlice

    layer breadSlice1 on Counter

    get breadSlice2 from breadBag as BreadSlice

    layer breadSlice2 on Counter

    get knife from drawer as Knife

    get peanutButter from peanutButterJar with knife

    spread peanutButter on breadSlice1 with knife

    get jelly from Cupboard as JellyTube with flavor jellyFlavor

    open jelly

    squeeze jelly on breadSlice2

    layer breadSlice2 on breadSlice1 by flip

  constructor end

object end  

Now I can make and cut my sandwich like this

get pbSandwich as PBSandwich

cut pbSandwich with knife at pbsandwich with width / 2

This tells the system to cut the sandwich at hand the width of the sandwich, except…the system has no idea what the width of a sandwich is, and crashes. I can get the width of a sandwich from the width of a slice of bread. There is a width property in the BreadSlice object, so I can get the width of the slice of bread, but which slice? Do I use slice 1 or 2? Do I use the bigger slice? Do I average the slices? As a developer I have to make a decision. I’ll add another property 

let width be 0 as Size

I’ll go with the bigger slice solution. I'll add some code if one bread slice is bigger than the other, set the width to the bigger slice. 

if breadSlice1 with width greaterThan breadSlice2 with width begin

  width from breadSlice1 with width

else

  width from breadSlice2 with width

if end

Now this code will work:

get pbSandwich as PBSandwich

cut pbSandwich with knife at pbSandwich with width / 2

And Yaaay!!! were done, You’ve coded a sandwich. Well… not quite. 

User Requirements

You deliver your sandwich and your user starts crying up a storm. He wanted wheat bread, strawberry jam and cut diagonally. Your code does not easily do that, and since you didn't ask up front, you have to not only make a new sandwich but change the code. 

There’s two more strategies that developer needs to do. The first is ask for specific directions. Usually that conversation goes like this: 

You: What do you want in your sandwich? 

User: I don’t know.

You try again, limiting the choices: 

You: Do you want raspberry or strawberry jelly on your sandwich? 

User: I don’t know. 

With your patience waning, You try one more time. 

You: Do you want raspberry or Strawberry jelly on your sandwich? 

User: I don’t know. What Kelly is having. 

Kelly is the user down the block who happens to be The User’s friend. You can do the research to get what Kelly is having or you can go to guessing since the user seems not to care about what’s in the sandwich, then wait for the complaints to come in and change the sandwich. 

Either way, there's something you should do: change the code so any sandwich can be made with any type of jelly, bread and peanut butter. The first line of the constructor would look like this: 

constructor with jellyFlavor pbBrand breadKind begin 

This code changes contains arguments that change the properties when you make a sandwich. Now you can make a sandwich with any combination of ingredients. 

get pbSandwich as PBSandwich with raspberry spiffyChunk wheat

cut pbSandwich with knife at pbSandwich with width / 2 rotate 45.0

That makes changing requirements from the user very easy. 

Debugging 

If you run our sandwich application once, it will work. Many coders stop here and say their job is done. Run twice and results get strange. You might get that sandwich. You might get no sandwich. You might get two slices of bread. You might get errors like this

warning: cupboard found empty

fatal: cupboard found empty 

This is a bug, an error in our program. I have jars and bread, so how can the cupboard be found empty? Ask a different question: How does the system handle jars once they have been used? Does the system close and put away jars? Does it leave them on the counter? Are there infinite jars in the cupboard? Are there two containers of some foods and one of others? You as a developer would need to know that, since behavior from the application might be different depending on the answers to those questions. There’s one more question: does those answers depend on whose house you make the sandwich? 

The bug is that once jars are used, they are left open on the counter, making it impossible for the system to grab them out of the cupboard. Finding that bug would require tracing the process and finding the false assumption about where the jars are. There might be different ways to handle that error or to interpret the code based on the error. Depending on the directions in the objects, you might have different behaviors for get in the objects we're using. The bread slice might ignore the lack of of a bread bag in the cupboard, and just get the bread if the bag is on the table. The peanut butter might complain about the lack of a jar in the cupboard with a warning and do nothing, and the jelly complains so much it shuts down the whole application with a fatal error.

You as a developer need to come up with a solution. I said you might have different behaviors in different houses. Some kitchens might have some system that puts everything away when done, some don’t. Some will give an error message when it does clean up: 

warning: Why can’t you clean up? It brings bugs!!!!

Often its best to just put everything away your self. You code to close the jars, tubes and bags, then place them back into the cupboard. 

close breadBag 

putBack breadBag

close jelly

putBack jelly

close peanutButterJar

putBack peanutButterJar

Running Out of Jelly

This solves one bug, but after using your program for a while you’ll get strange results again: your jelly is empty. Is there another tube in the cupboard? Do you have to stop the program and crash or do you pause and get more jelly? Or do you jump over that one step and complete the others while getting the jelly? 

There’s lots of solutions to this problem. One solution is to check the supplies of jelly at the beginning of the operation. If you have a low amount, check if you have another tube. If there is not another tube, start another program, let’s call it TeenWithCar that goes to get the extra tube. If the tube is empty, dispose of the first tube and use the new tube. There’s still some problems to solve with this solution. 

System Resource Changes

TeenWithCar comes back with a jar instead of a tube of raspberry jam. Our whole application depends on using a tube of jelly and not a jar. The app crashes since this is not a jar. 

fatal: type ‘JamJar’ is not type ‘JellyTube’

You have to recode for this and use a jar. You might add logic to check if it is a jar or tube so this doesn't happen again. 

System Problems

Timing is everything. Remember this step: 

layer breadSlice2 on breadSlice1 by flip

Flips the bread so the jelly side is down. That assumes the the bread flip is fast enough to keep the jelly on the bread. If too slow it slips off. How do you deal with slow flips? 

Similarly, if the TeenWithCar fails to get the jelly or jam back to the cupboard fast enough and you make several sandwiches, you might still run out of jelly. It might not be TeenWithCar’s fault: traffic might be heavy or the grocery store is very busy. 

The timing scale of making a sandwich may not be the same time scale as some other operations, like getting extra supplies. The sandwich takes five minutes while it may take an hour to get extra jam from the store. How does that affect the program, and how do you get things moving smoothly and seamlessly?

The Point of All This

For most people who have been in IT or application development, you’ve figured out I described just the tip of the iceberg of issues we deal with when developing applications. Coding a peanut butter sandwich is hard, but that's only one step in the process. Getting program requirements, planning, coding, debugging, documenting, maintaining, and supporting are all steps. If you’ve never programmed before or just started coding, this is what professional programmers live with — lots of problems, problems which do not go away, require constant changes, and will always leave someone screaming at you and your work. It’s not uncommon for the same compilers on two different operating systems to act differently, nor is it uncommon for hardware sensors, server and Internet resources to take far too long to get data back to the application. Different versions of languages handle closing or putting things away differently. Users and management have no idea what they want as an application, and cannot tell you. Users will find ways to crash or corrupt the system you never dreamed of — then scream at you about it. The compiler developers change the language on you, some like Apple’s Swift do it once or twice a month, changing jars and tubes almost randomly, then totally mess everything up every June with a new version for chocolate hazelnut spread and bananas. For those in database programming, one day your database looks one way, a month later it loses two fields and gains three. This is the day to day existence of developers, programmers, and the IT people who have to maintain all this. 

The real issues in computing is not knowing one language to code. It answers a lot more critical questions. How do computer languages work and work differently from each other? How do systems move data from one place to another? How do users interact with systems and what do users really want? How do you scale a function efficiently? Just knowing Java, Swift or SQL is not enough. I’ve read those ultra hard quizzes asking about obscure features of some language given during interviews. If I was ever given one of those on a job interview, I 'd probably ask for the docs. If I could't look up the answers, I'd leave the interview. First of all I’m guaranteed to fail the test — I let my documentation library keep all that organized for me. I look any of those questions up, then prototype to understand it and make sure it works. Secondly, that’s the sign of a bad shop for me, one likely to fire me at the next compiler update for being obsolete or one that has no clue what the real talents are of developers. If someone would give me a thought experiment like this one, to develop a compiler specification for making peanut butter sandwiches*, I’d stay and be intrigued at the position. 

Programming is some of the hardest problem solving there is. Programming's rigorous removal and prevention of bugs has bled into other industries: Much of quality control and lean iteration methods come from programming. Systems with very strict quality standards like medical devices or aerospace adopted many of the tools we use to prevent or eliminate bugs. While other industries can take or leave the intensity of the problem solving, programming must do it for every social media app, game, financial report, dashboard and medical device controller.

This brings me back to the core theme I’ve been trying to get across over the last few weeks in my posts. Problem solving needs a huge knowledge base, one that requires one to always be learning, and learning skills for continuous learning. Knowing how to look up answers in documentation, and being able to read the documentation is a key skill. Being able to prototype, to play, with code to understand and learn how functions really work after looking at a documentation reference is another skill. Being ready to change and learn at any second, and being able to look at the same problems from many different angles is important. How much of our culture and workforce have the ability? I champion teaching programming skills in school, because those skills no matter what one does will be beneficial. I am a big advocate of apprenticeships, since many skills seem to be badly taught or cannot be taught in classrooms, and the environment where they are used in context facilitates learning. I don't think most people are cut out to be programmers. It requires intense brain power. You really have to enjoy that intensity of thinking in a way littered with problems if you are going to be good at it. When you are not thinking, you are learning. Who really wants to do that? 

Think about that when making your next peanut butter sandwich. 

Steve Lipton is the mind behind makeapppie.com for learning iOS programming. Besides Chief Information Officer at Scientific Device Laboratory, he is the author of several books on iOS programming and development. He is also a LinkedIn Learning author. His latest courses are SAP Business One Essential Training and iOS and watchOS Development:Notifications. He also is the author and host of iOS Development Tips Weekly.

#AlwaysBeLearning

*I want to put a comment for all those compiler developers out there. This theoretical language gives me shivers thinking of how to write the compiler. That constructor would be a nightmare by itself with the implicit initialization of properties, and the with modifier signifying a parameter list just gives me a headache to think about parsing nested functions in parameters. My point was explaining computer languages without getting into the issues of one that already exists for the uninitiated. In an interview, based on my first question “who will be using this language?” I would write it very differently. I based it on people who would not know some of the C-based conventions like semicolons, curly braces for blocks, parameters/arguments in parentheses and dot notation that permeate most modern languages and scripts. If the answer was conventional programmers, this would have those symbols and probably would look a lot like Swift, Java or C++. As it is, its a bad combination of a strongly typed older language like a reverse Objective-C, with some LISP, Pascal, and hints of COBOL. I was seriously thinking of adding DIVISIONs like COBOL, but I don't believe in cruel and unusual punishment, and it did nothing to prove my point.  

Shreyes Kejariwal

Founder & CEO at Keyideas

7 年

As technology advances, many simpler languages have come into place such as Swift instead of Object C for iOS. These languages are simpler but mind it they are simpler in comparison only. Developers whether be an app or a website, the demanding future always has something new in the kitty. It's only the urge to take up new challenges and deliver more future-ready products that keep developers going on & on!

Peter Smith CEng

Senior Electronics Engineer at Chess Dynamics

7 年

Excellent read: I remember doing the same sort of thing when teaching (I was the 'robot' - they had to control me to walk across the room). I managed to avoid serious injury by complaining the instruction was invalid on quite a few occasions. Seriously, the same skills are just as necessary for just about all design work.

Awesome article showing non software developers why they have to be specific enough in order to make software developers life not a burdensome. This article shows all the struggle software developers have while developing starting from the lack of requirements specification with it's pitfalls and ending in all the assumings users have that the software developers should know about.

Peter Wibeck

Principal Tech Lead / Architect ?Quality driven ?Business focused ?Divemaster ?.Net/C# ?Azure/AWS ?MongoDB/SQL ?REST API

7 年

Good article.. it's a really interesting way to explain what we as coders do everyday to none tech friends.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了