My formal education is as a computer scientist. I was lucky enough to get this education at one of the cusps of computer development.
My Mentor went through a computer science program just 4 years earlier. There were classes he took that were not even offered in my program. The change was in moving from a need to understand hardware to a place where understanding the hardware wasn’t important to computer science.
What this meant was that my Mentor could design a computer from the gate up, I still struggle with hardware design.
My education included understanding low level instructions, instruction timings, bit manipulation, data structures, semaphores, and a host of other low level concepts. It also included much higher concepts.
At the time, my program included a year-long class where we wrote a working compiler, a required class where we wrote an operating system, as well as all the languages of the day. We even had theory class, such as the class on proving a program correct.
In addition to the formal classes offered by the University, I participated in an intense 8-week course where I was taught how to apply the classroom theory to actual working systems. This was the “systems class”. It started at 0800, ran through to 1200 with no breaks. We had a lunch break from 1200 to 1300. Then classes continued from 1300 to 1700.
We had to turn in our assignment of the day at 0800 the next morning.
This is what a day was like on the third week.
Wake up on a table in the student workroom of the computer center. Collect our work, stumble next door to start our 0800 classes. At 1200 hoof it out of there 2 miles to the house, shower, change clothes, move it back to the computer center and get there before 1300.
Being more than 15 minutes late was failure.
Study and learn new topics from the instructors. At 1700, head out to get dinner. Get back to the computer center by 1830. Work on assignments and projects until the computer was shutdown for nightly maintenance at 0400.
Decide if an hour of travel time to sleep in a real bed was worth more than an extra hour of sleep. Claim a table and fall asleep.
Repeat the next day.
Structured Programming
It is hard for a modern programmer to understand what a huge breakthrough the concept of “if-then-else-end if” was. It was the first of our structured code.
Before we had that type of language structure, we used “if condition goto”. Which was even more confusing when you used the FORTRAN IV construct of “IF value goto1, goto2, goto3” where the goto used was based on if the value was negative, zero, or positive. And yes, there was way too much code that used that instruction.
I helped my father with his MBA by being the computer dude to help him with the software he was using. It was written in FORTRAN IV or III. It wasn’t uncommon to be reading a section of code, have a GOTO that took you somewhere else in the code, which then had another goto back to the place where we just were.
In some cases, the code would conditionally jump to that “patch” which would then jump back into the other routine. It was a mess.
if condition then
do something
else if condition then
do something else
else
do something entirely different.
endif
Structured programming has at its base the concept of block correctness. Every block must be well-defined, do one job, have a set of well-defined inputs and outputs.
If this is satisfied, then you can verify the correctness of that block. You can test the set of acceptable and unacceptable inputs and verify that you get the correct outputs. If this test succeeds, then you can mark the block as ‘good’.
You can combine blocks in sequence. If you are connecting blocks, then the preceding blocks must contain all the outputs that will be used by the following blocks.
You can use conditional structures to create a block that is composed of verified working blocks.
Building from Blocks
One of the things about using blocks, is that you can build iteratively.
To give an example, I am working on a website. The front page has a carousel of rotating “hero” images.
From this, I knew I needed to be able to upload an image. The carousel has a fixed aspect ratio, this meant that I needed to have images in this aspect ratio. I also know that the user will want to decide what part of the uploaded image they wanted to use for the hero image.
In simpler terms, this means that I needed the ability to apply simple cropping to an uploaded image.
There is a black box in my head. It has defined the “cropper” block to take as input an image, the current cropping, and the current canvas to draw on. That block will interact with the user. When the user finishes, the block will output (x, y, width, height) in image pixel coordinates.
There is a different block box that takes two HTML elements and uses them to generate the required inputs to the crop block.
Another block takes the output from the crop block and turns it into a post to the server, to update those values.
Here is the thing, I’m using an obsolete cropping package because it is “simpler” while I’m extending my TypeScript and JavaScript knowledge. But I will be able to replace it with a very modern version and none of the other code will break, as long as the inputs and outputs do not change.
Currently, when you save your changes, the code submits the modifications as form data, which causes the page to reload.
Piece Wise Progression
What this means to me is that I’m constantly testing small changes. I will write a block of code, compile, deploy to the test server, test the results, edit some more.
Saturday was a lazy day. I only performed this cycle 50 or so times.
Every time I get a block working better, I make a git
commit.
Friday, I had a breakthrough. I managed to make drag and drop work to select a file for uploading. Created a thumbnail of it. This was all via simple HTML and TypeScript.
Progress was fairly slow on this, learning curve, but what I found interesting is that I would get to a point where I had a working image selection, and only then realize that I had not connected the save button to anything.
Once that was working, the edit process turned out to be more difficult than I expected. It was all working from before, but I needed to hook into the code from a different place. But because that edit process had well-defined inputs, it was merely a matter of creating the correct inputs and it all “just worked”.
Of course, once I click that save button, I found out that I wasn’t actually uploading the image. Which was another thing to fix.
That worked, and it turns out that the server side needed some changes.
But everything was working because the process was all about blocks. When a block didn’t do what I wanted, it was a simple process of checking the inputs for correctness, then checking the output for correctness. If input was good and output was bad, it was a logic error. Track it down and everything gets fixed.
Working On The Railroad
When we code, it is not uncommon to find that there is some exception which can’t be processed by “normal means”.
For example, you are expecting an integer, representing the width of something. You get a string instead.
You could go down a new path, or you can convert the string into an integer. I.e. “768” becomes, simply, 768. That’s simple enough.
But what happens if instead of 768 you get “100%”? The answer could be to go down a separate logic path to deal with width, height, x, and y as percentages.
The railroad method says that you treat the code as a railroad line.
There is the mainline, it is a single track running between two cities. If you have a fast train on the tracks, and it is followed by a slow train, that fast train will get held up behind the slow train.
The answer to this is sidings. Much like the passing lanes on a two lane road, a siding is used to shunt one train out of the way while another train passes.
When the fast training is getting near, the slower train is shunted onto a siding. It waits there until the fast train has passed, then continues down the siding and back onto the mainline.
You can write code this way. When there is a problem with the input, you are being shunted onto that siding. You can decide there is no way to continue and abort, throw an error, you can do something else to get yourself back on the mainline.
Using the “100%” example, the siding means that we will do whatever code is required to convert the 100% into an integer that meats the requirements. This could be as simple as looking up the size of the parent and using that size.
The 100% could mean that we want this size to be a match for the other size. I.e., if the natural size of the image is 640×480, 100% width could mean 640. It could also mean that if we scale the height to 75% of natural height, we want to keep the width as 100% of that natural height.
These logical choices are done, and that conversion takes place on the siding. After the conversion is properly completed, the code can join the mainline again and nothing on the mainline needs to change.
Leave a Reply