Gunnar's blog - Programming

Finding Null or Empty String Checks in Java


I have a lot of code like this:

if( null == myString || "".equals(myString))
    doAThing();

The codes fine, really. Very idiomatic, gets the job done.

Lately, I've been on a predicate kick. I like replacing my conditionals with predicates that describe the intent of the conditional statement. I can also replace a lot of my boilerplate code with a couple common predicates. This is one I use a lot:

 public static final Predicate<String> NULL_OR_EMPTY = (in) -> null==in || "".equals(in);

It's true; if the code base had been crafted with Optionals or other null-avoidance techniques, that I could mostly ignore this check. My code base has history and weight and external interfaces, so there are plenty of this check and others like it.

I have been using this regex to find likely places that I can apply my predicate:

  /\(\s*null\s*==\s*(\S*)\s*\|\|\s*""\.equals\(\s*\1\s*\)\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*""\.equals\(\s*\2\s*\)\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*\3\.equals\(\s*""\s*\)\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*\4\.equals\(\s*""\s*\)\s*\)|\(\s*""\.equals\(\s*(\S*)\s*\)\s*\|\|\s*null\s*==\s*\5\s*\)|\(\s*(\S*)\.equals\(\s*""\s*\)\s*\|\|\s*null\s*==\s*\6\s*\)|\(\s*""\.equals\(\s*(\S*)\s*\)\s*\|\|\s\7\s*==\s*null\s*\)|\(\s*(\S*).equals\(\s*""\s*\)\s*\|\|\s*\8\s*==\s*null\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*""\s*==\s*\9\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*""\s*==\s*\10\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*\11\s*==\s*""\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*\12\s*==\s*""\s*\)|\(\s*""\s*==\s*(\S*)\s*\|\|\s*null\s*==\s*\13\s*\)|\(\s*(\S*)\s*==\s*""\s*\|\|\s*null\s*==\s*\14\s*\)|\(\s*""\s*==\s*(\S*)\s*\|\|\s*\15\s*==\s*null\s*\)|\(\s*(\S*)\s*==\s*""\s*\|\|\s*\16\s*==\s*null\s*\)/g

It's an ugly one, but it matches a lot of common patterns for checking if a string is null or empty. I'm hopeful that folks will suggest either a better way of finding null or empty checks or help me add to my hideous regex!

How to Setup Cucumber Junit Tests for Java


cucumber growing

Java cucumber tests have 3 parts.

  1. The feature file
  2. The step definitions (the 'glue' file(s))
  3. The junit test file

The feature file is where the behavior, or business, oriented description of the test goes. These files are written with Gherkin syntax.

When writing Cucumber tests, write the feature files first. Have stakeholders, business owners and users write them, if that is possible in your organization. If not, the lead developer and/or project manager should write the feature files with the stakeholders, business owners and users. Put the work in and collect their stories and scenarios.

After the feature file is written, write the step definitions. This is what the Cucumber people call "glue" code. It's essentially regexes, setup and test code.

Finally, to make running the Cucumber tests easy in Java, write a junit wrapper class. This is an empty, annotated class that will enable Cucumber to use junit's test machinery to run Cucumber tests.

Example

I've created an example project. Cucumber uses Maven, so we have a project with a standard Maven directory structure:

+---src
|   +---main
|   |   +---java
|   \---test
|       +---java
|       \---resources

Organization

Let's put the feature files under test/resources. Let's put the junit classes under the principal test package - in this case com.gunnargissel.cucumberexample.artfight. Let's put the step definitions (the 'glue') in a stepdefs package under the principal test package - com.gunnargissel.cucumberexample.artfight.stepdefs

Behavior Scenario

For this example, we are creating the business engine for an art supply store. Let's use Stuart Semple as an example. Stuart Semple sells "Black 2.0 - The world's mattest, flattest, black art material" to everyone except Anish Kapoor. There's a bit of a backstory.

Here's what a feature file might look like, no_anish_kapoor.feature:

Feature: Stuart Semple will sell Black 2.0 to any artist except Anish Kapoor

    Scenario Outline: Anish Kapoor tries to buy Black 2.0
                       Given a purchaser, <purchaser name>, Black 2.0 is <saleable>
                       Examples:
                                 | purchaser name    | saleable     |
                                 | Anish Kapoor      | not saleable |
                                 | John Doe          | saleable     |
                                 | Jane Doe          | saleable     |
                                 | Manish Kapoor     | saleable     |
                                 | Anish Kapoorski   | saleable     |

With the feature file, it is time to create the step definitions, or 'glue' code. In this case, create src/test/java/com.gunnargissel.blacktwopointoh.stepdefs.NoAnishKapoor.java

The scenario outlined above only uses one keyword, "Given". That means we will create a single method in our step definition "glue" class that uses the @Given annotation. The objective of the @Given annotation is to write a regular expression that matches the scenario. In this case we want the <purchaser name> and the <saleable> qualities to be variables we pass to the stepdef. The rest of the @Given is business language that we pattern match on. The stepdef performs the role of a typical junit test, along with the setup for the test.

@Given("^a purchaser, (.*), Black 2\\.0 is (not saleable|saleable)$")
            public void testWhoCanPurchaseBlackTwoPointOh(String purchaser, String saleable) {
                    boolean actual_saleable = BusinessRules.saleable(purchaser);
                    boolean expected_saleable = saleable.equals("saleable");
                    assertEquals(expected_saleable, actual_saleable);
            }

Now that there is a scenario and a step definition, a junit runner class needs to be created like so:

@RunWith(Cucumber.class)
@CucumberOptions(
                       format={"pretty", "junit:target/cucumber/black_two_point_oh_sales_list.xml"},
                        features = {"classpath:no_anish_kapoor.feature"},
                        glue={"com.gunnargissel.cucumberexample.artfight.stepdefs"}
)
public class TestBusinessRules {}

After creating the necessary files, running mvn test will execute the feature scenario created above. Maven will create black_two_point_oh_sales_list.xml in the target/cucumber directory.

Further Resources

Just Enough Regular Expressions for Cucumber is a great guide that explains how to use regexes (and Java) with Cucumber. The same author also made a cheatsheet that is a helpful reference.

The artfight example code.

Thank you Ali Burçin Titizel for the header image

Java 8 Functional Interface Cheatsheet


O'Reilly has a great in-depth article on the functional interface in Java 8. Here's a quick cheat sheet to keep track of the main types, what they do and common use cases.

Predicate

A Predicate returns true or false. These are used for filters or replacing big chains of if/else logic.

Function

A Function transforms data. These are used for maps, and other transformations. It is important to not mutate the original data that is passed in.

Supplier

A Supplier takes no arguments and returns a value of a known type. Fetching, reading or creating resources to be used by other functions are common use cases. Suppliers get things started.

Consumer

A Consumer accepts a single argument, but does not return any results. Consumers are where mutating functions, things with side-effects, should go. Consumers finish things.

How to Write a Multiline Lambda in Java 8


For the most part, single line lambda functions are all you need.

This is a single line lambda:

Predicate<String> isBig = str -> str.length() > 10;

Sometimes one line is not enough to express the complexity of the lambda. How do you make a multiline lambda?

This is how:

//set up - elsewhere in the pseudo code:
public Sound getSound() throws MuteAnimalException {...}
Predicate<Sound> barks = sound -> Sound.valueOf("bark").equals(sound);


//payoff, a multiline lambda
Predicate<Animal> isDog = animal -> {
    try {
        return barks.test(animal.getSound());
    } catch (MuteAnimalException e){
        logger.severe(e.getMessage);
        return false;
    }
};

How to Execute Python Files in Windows CMD


A question I get from new devs fairly frequently is "After installing Python, I can't run Python scripts with 'python script.py'. What is going wrong?"

The most common thing going wrong is that the Python executable is not on the path.

Add Python Executable to the Path

Let's assume your Python executable is C:\Python37\python.exe. Open up cmd.exe and type setx path "%path$;c:\Python37;c:\Python37\Scripts". This will add both python.exe and the helper scripts included with Python (like pip and easy_install) to the path.

After modifying your path, open a new command prompt (this is important - Windows won't refresh the path in an open command prompt) and then try it out by navigating to your script directory and typing python script.py. You should see your script run!

How to Move Mercurial Changesets to a New Branch


This is me when I realize I've been committing in the wrong branch all day:

via GIPHY

Fortunately, there is a way to move changesets between branches, provided you haven't pushed your changes out to any other repositories. If you have pushed your changes out to the mothership repository, or your coworkers, the only way to pick up the pieces is to get all the users of the repo together and coordinate. That is beyond to scope of this article, and too hard for all but the smallest or dictatorial teams.

Problem Setup

Here at Reci-p.com we make recipes and sell advertising. I've been hard at work writing up new recipes. Here's what I start with:

$ hg history
changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

changeset:  0:6f5ef8f77a9f
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Jan 28 11:30:00 2017 -0900
summary:    Added taco recipe

I want to add drink recipes, but they aren't ready to show to our valuable users, so I want to add them in a development branch. Unfortunately, I've been out all night sampling drink recipes and I forget to create the new branch!

via GIPHY

This is what I end up with:

$ hg history    
changeset:  3:398dfc004270
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added michela recipe

changeset:  2:852364587134
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added mojito recipe

changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

changeset:  0:6f5ef8f77a9f
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Jan 28 11:30:00 2017 -0900
summary:    Added taco recipe

Fortunately, before I push all my code to the mothership repo, I notice I've been working in the wrong branch.

Move My Code to a Different Branch

The approach to fixing this problem is to create the missing branch, then create a patch from my new changesets, apply the patch to the new branch, then strip off the changesets from the incorrect branch.

Working in the same repository as above:

$ hg export -o patch.diff 3 2

I create a patch, which shows up as a file called patch.diff.

Create a branch off the newest changeset before I started adding new stuff:

$ hg update 1
$ hg branch drinks
$ hg commit

Patch the new branch I just made:

$ hg import patch.diff

This is dangerous, so make sure you are targeting the correct changeset. You can't easily undo this. You also need to have the strip extension enabled

Remove the changes off the original branch, so we can avoid mixing drink and food recipes:

$ hg strip 3

Phew, my repository is fixed. Now when look at the history I can see that I've got everything in the correct branch:

$ hg history -l 4
changeset:  4:8afca56c4940
branch:     drinks
tag:        tip
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added michela recipe

changeset:  3:357ff9545db
branch:     drinks
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added mojito recipe

changeset:  2:357ff9545db
branch:     drinks
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    drinks branch

changeset:  1:b1c330d2ac79
user:       Gunnar Gissel <gunnar@reci-p.com>
date:       Sat Feb 04 05:15:00 2017 -0900
summary:    Added quesadilla recipe

Now I'm safe to push and I won't accidentally pollute our recipes with drink mixing recipes

via GIPHY

Organic Artisanal Small Batch vs Impersonal Factory


There are a couple approaches a shop can take to coding. There is the "Organic, artisanal, small batch" programming and "Impersonal Factory" programming.

My transition from a small batch artisanal shop to an impersonal factory is recent. The change is still fresh in my mind, while distant enough to have some perspective. This is what the change from an artisanal shop to impersonal factory style coding is like:

Small shops and new devs take the organic route. This is out of necessity. You have to move fast, get code out the door and put food on the table and money in your pocket.

Infrastructure is the other reason. Small shops and new devs often have little infrastructure. There may not be a build server and a staging server. There may only be a laptop and production.

Organic, artisanal, small batch programming gets things done. If nobody sat down and banged out the first version of something we'd still be using slipsticks and filing cabinets. Moreover, while teams are small and have low turnover, the cost to artisanal coding is minimal. If the author is there and can personally explain where the bodies are buried to a small group, style differences are barely a hiccup.

As shops grow, the merits of factory style programming becomes clear. I don't mean the factory pattern. You GOF people can see yourselves out :)

At some point, around 5 developers, the overhead from introducing new developers begins to drag on the team's productivity. Different project formats and project that need leads to be responsible for building applications

The Solution

Stand up some shared infrastructure and standards. Pairing shared infrastructure and standards together is absolutely necessary. Without standards, shared infrastructure turns into a shared nightmare. Without shared infrastructure, you're left with standardized coding wizards mumbling into their own private grimoires. No chance of telling whether or not the standards have been adopted.

The bare minimum for effective shared infrastructure is:

  • A shared code repository

  • A shared build server

  • A place to put artifacts created by builds

  • A staging server for testing deploys before they make it to production

What each of these elements mean is different depending on the stack your shop uses. I'm in the boring enterprise world, so the stack I'm going to talk about is Oracle, Java and Javascript.

Shared Code Repository

The specific tool your team uses doesn't matter, so long as it uses a tool and you are all storing code in the same place. According to Zero Turnaround's 2016 tech survey Git (and presumably Github) is the clear winner for version contro, followed by Subversion. My shop uses Mercurial - we made the switch from CVS before it was clear that Git would win and haven't felt a strong need to switch just for the sake of switching.

I won't go into why you should use source control in 2017(!), but I want to emphasize that having a shared place where every developer gets and puts code is extremely valuable.

If you aren't using source control, get with the program.

Shared Build Server

In 2017, developers should not be deploying artifacts to production that they create on their workstation. Partly this is because it can be difficult to recreate the state of a dev's workstation at a point in time well enough to recreate the artifact. Partly because manual processes like these have a poor bus factor and are difficult to pass on to new devs. Mostly because computer time is cheap, dev time is expensive, and automation is easy.

A build server provides a place to define the build and deploy process. A place to record past builds. A place to store artifacts. One of the great benefits of a shared build server is that any developer can run a build by pressing a button. It's a powerful advantage when a new hire can do a build on the first day they walk into the office.

Keeping deployed artifacts around another great benefit. If you need to roll back, you can get the exact thing that was deployed last. No manual copying out to a network file share, no wiki notes about where to find the backups. Everything in one place!

Another benefit to a shared build server is that they can act like a friendly interface to cron. Devs can easily script a repetitive task and then pass off the scheduling to Jenkins. Jenkins keeps track of the scripts success and can email devs when there are problems. It sounds small, but this is a huge productivity booster.

Artifact Repository

A build server can hold all your artifacts, up to a point.

Build servers are good at holding artifacts that get deployed to production servers. Build servers are not good at holding intermediate artifacts, like library jars.

Jenkins can move jars between builds, but keeping track of which jar belongs to a build is difficult. Like spaghetti code, spaghetti dependencies end in tears.

Developers need access to intermediate artifacts, but build servers do not give easy access. Sane Java development uses Maven, which can go get jars, but it needs to get them from a repository, not a build server.

Nexus is the artifact repository my shop uses. It's an artifact repository that works with Maven and is self-hostable. Thanks to Nexus, new projects can use existing libraries and tools without really trying.

Staging Server

A staging server is a server where code is deployed before it is deployed to the production server. The simplest way to use a staging server is as a simple smoke test - deploy your code, boot the server. If it boots, A-OK.

A staging server can also be a test server. Deploy code to the staging server, then run functional and user-acceptance tests. The staging environment is designed to be just like production.

If you aren't using a staging server, you're staging/testing in production, which is dangerous.

Reflections on the Change from Small Batch to Factory

=======================================================

The change from a small batch shop to a factory shop has been positive. The speed of code development increased and the rate of recurrent bugs decreased. The straightjacket of Maven, and the Nexus repository encourage modularity and code sharing, which allows us to build more complex systems quickly.

On the negative side, the speed of creating a new project with new infrastructure decreased. More infrastructure for testing is required, which naturally increases the burden of rolling out new projects.

Increased automation has offset the infrastructure burden, but more automation remains to be done.

I wouldn't go back.

Maven for Ant People


Ant people often find Maven completely mystifying. I know I did. I work at a shop that was, until recently, completely Ant based. We built complex build scripts, and we did with XML, the way God Intended™.

With Ant, you start from scratch. You can have your project set up any way you want - the directory structure is up in the air, the artifacts you want to create through building are up in the air, you can even bring in conditionals to change the build based on whatever you want. In short, Ant is the wild west.

Maven, by contrast, is a straightjacket. Maven requires your projects are laid out in a particular way. You have to use approved Maven directories, and you have to do things within the Maven lifecycle. You can't just write a bunch of targets and stick them together any old way like you can with Ant. Coming to a Maven project from the Ant world is very confusing, because the directory layout is slightly different, it isn't immediately obvious where dependencies are kept and when you try to run the pom as you would a build.xml, nothing happens. Maven calls much of this behavior "convention over configuration" which makes things about as clear as mud.

Project Layout

I think the first thing Ant people notice when they come to Maven is the directory structure is strange and different. Most Ant projects I've worked in have a layout something like this:

src/
|- com/
   |- example/
      |- package/
        |- packageclass.java
        |- test/
           |- test.java
      |- otherpackage/
         |- otherclass.java
         |- test/
            |- testother.java
deps/
|- guava.jar
|- jodatime.jar
app.properties
test.properties

Maven projects have a strict layout, like so:

src/
|- main/
   |- java/
      |- com/
         |- example/
            |- package/
               |- packageclass.java
            |- otherpackage/
               |- otherclass.java
   |- resources/
      |- app.properties
|- test/
   |- java/
      |- com/
         |- example/
            |- package/
               |- test.java
            |- otherpackage/
               |- testother.java
   |- resources/
      |- test.properties

Maven expects code and resources to be in the src directory. It further expects the src directory to contain a main and a test directory. Things in the main directory are headed for the ultimate artifact. Things in the test directory are test data, or junit tests, or other test code that we don't want to include in the ultimate artifact.

Both the main and test directories have java and optionally resources directories. Java code goes in the java directory - this is assuming you are developing in Java, of course :)

Resources, like properties files, or test data, or FindBugs exclude files go in the resources directories. If your project doesn't have any resources, you can dispense with these directories entirely.

The trick with Maven vs Ant is that Maven absolutely requires this directory structure. It plain won't work without it. At first, to my Ant eyes, that seemed needlessly constraining, but the more I've worked with it, the more I've liked it. Any Maven project has the exact same directory structure, so as soon as you open a Maven project, you know where everything is, regardless of who developed it. In my Ant shop, it helps to know who the first developer was, because each dev lays out a project slightly differently. While this gives the code a charming old world village feel, it makes ramping up new devs a slow process.

Lifecycle

Ant is made of build targets. Targets are an xml element that define a unit of Ant behavior. Targets can depend on other targets, so you can have some kind of order to your build. You can define any number targets and call them whatever you want, and set up whatever hierarchy of targets makes sense to you.

All that said, I think this might be a 'normal' structure for an Ant project:

<project>
    <path></path>
    <target name="init"></target>
    <target name="compile" depends="init"></target>
    <target name="test" depend="compile"></target>
    <target name="jar" depend="test"></target>
    <target name="javadocs"></target>
</project>

Reading over a Maven pom reveals no clues at all as to what the happens during the build. Here's a schematic example to contrast with the Ant example above:

<project>
    <meta stuff defining the project></meta stuff>
    <build></build>
    <dependencies></dependencies>
</project>

That's it! When I first read a pom, after years of reading Ant build scripts, I couldn't figure out what was supposed to happen. It doesn't look like anything is going on - no compiling, no jarring, certainly no testing.

Again, as with the directory structure, Maven is implicit. It has what Maven folks call a "lifecycle."

Vocabulary Lesson

Ant people will benefit from a short vocabularly lesson to get up to speed with Maven jargon, and to get some googleable terms

  • Lifecycle
    • The process for building and distributing an artifact
    • There are three lifecycles
      • default
        • Default builds and deploys your project. Deploy might trip up those of you who are used to putting things on servers. In this case, it does not mean 'deploy your webapp to Tomcat.' Instead it means 'deploy the build artifact to a Maven repository'
      • clean
        • Clean cleans up your project, by deleting build products and intermediate clutter that the build creates. In practice, this mostly means it deletes the target folder that Maven builds create
      • site
        • Site creates the documentation for your project's website. Javadocs, junit test reports, findbugs reports and that sort of thing happen in the lifecycle
  • Phase
  • Goal
    • Phases are made out of plugin goals. If you read through a pom, you'll notice that build element, for example, often contains a number of plugins. These plugins define what happens in a phase.
    • Goals can also be used independently of a lifecycle, to allow for a finer grained control over the build

Using the Build Lifecycle

To use the build lifecycle, use the Maven command line and pass in any lifecycle, phase or goal name.

Examples

  • Lifecycle
    • mvn default
  • Phase
    • mvn package
  • Goal
    • mvn dependency:copy-dependecies

You can pass multiple lifecycle arguments to maven and it will execute each of them in turn

Dependencies

Dependencies is one of those places where I don't think there is a standard Ant way of dealing with them. Ant people all know the labor of keeping the jars their project depends on in synce, and how hard it can be to distribute these jars to a team of developers.

I've seen a couple approaches in the Ant world. Sometimes there is a folder on a shared network drive, and in the wiki for the project it tells devs to grab that folder and put it somewhere special relative to the project. Other times, the jars are just included with the source code in the repository as big binary blobs. Sometimes the jars get their own version control repo and devs are supposed to put the dependency repo in the same directory as their project repos, so all dependencies can be reached as ../dependency.jar. I've even seen a giant zip file with a pre-configured directory containing eclipse with all the projects set up and a directory of the repositories included in a known location, with a script for syncing the known location with a central server.

My point is, Ant doesn't really have a way of dealing with the problem with dependencies. Regardless of what you are doing, you also have to manually compile the classpath and include that in your Ant file, which is another interesting excercise.

Where Maven shines, and what initially brings people to Maven, is its dependency management. Maven will go get all your dependencies for you, build a classpath and can even make a fat jar for easy releases. The only thing you need to get all the dependencies for a project is Maven, and a pom.

Maven also has the concept of "scopes" for dependencies. Indicating that a dependency (like junit) is only required for the "test" scope will leave out that dependency when building the final artifact.

Basic Maven Usage

Knowing how Maven is supposed to work is all well and good, but how do you actually use it?

In most cases, a developer probably wants to make a jar, war or ear that they can use or give to someone.

Here's how you do that from the command line:

mvn package

From Eclipse, right click on the project and select 'Run As' > 'Maven Build'. The first time you do this, it will bring up the Run Configurations dialog. Type 'package' into the goals field and run the build.

You might notice that these commands don't run the whole default lifecycle. They skip the verify, install and deploy phases. Those are a little more complicated than the basic differences between Maven and Ant that I try to illustrate here. A separate article is probably required to talk about what installing and deploying mean in the Maven world, how they interact with IDEs and when they should be used.

How to Make a Program - Answering 12 Year Old Me's Question


When I was little, I loved computers. I thought they were terrifically cool and I really wanted to make them do things. I had a steady diet of cyberpunk and video games, so I had this idea that you could do anything by jockeying a keyboard and programming. What I didn't have was any knowledge or resources to get me going.

I wanted to write a program, but I didn't know anybody who had every programmed and the programming books I was able to get my hands on were either ancient and useless (think COBOL manuals) or new and useless (think a late 90's C++ doorstopper).

I had some idea of how to program. You write special words, in a really specific order and the computer does something. Like an excel macro, or something. What I didn't know is how to turn my words from a text file to something a computer could run. Like the literal mechanical process of programming. I was clueless about how to do that.

This was the 90's and I was in Alaska. Not rural Alaska, but definitely the sticks. I had the internet, or some reasonable facsimile if nobody needed to make a phone call. I read Slashdot and forums. I could read a lot about programming - how people were doing it, what languages were out there, some tutorials here and there.

Everything I read was missing the fundamental steps of how to program. Sure, they'd tell you how to say "Hello world", but bootstrapping myself up that high with no support network was just not happening. Maybe I was slow, or I was overthinking it. It seems like computers are complicated, so why wouldn't creating a program be complicated?

Let me cut to the chase and answer 12 year old me's burning question. Just in case there is someone out there with the same problem.

How to Make a Program in 4 Simple Steps

  1. Open a text editor. A text editor is something like Notepad in Windows or Text Edit in OSX.
  2. Write a program. Just copy paste a hello world program from a tutorial for this step
  3. Save this file as a plain text document
  4. This is the complicated part that confused me as a kid. There are two options, depending on the type of language you are using
    1. Compile the file you just saved with a compiler. Run the resulting executable
    2. Run the file you just saved with an interpreter

That's it! All you have to do is write a text file and feed it into a compiler or interpreter and you have a program. Seriously, don't over think this part

Compilers and Interpreters

I used a couple specialist words in step 4. 12 year old me had read them, but didn't understand them, so I better explain.

A compiler is a thing that takes the text file you wrote and translate it into machine code your computer understands. Compiled languages are things like C, Java, Go, Rust, etc.

An interpreter reads the program directly out of the file (or sometimes the command prompt) and 'interprets' the instructions on the fly. Interpreted languages are things like Python, Javascript, Ruby, Lisp, Perl, etc.

Some languages have both a compiler and an interpreter.

The Fine Points

It turns out that each language has a variety of fine points, or variations on the 4 steps I listed above. I'm not going to try to explain everything in a single blog post, but I'll hit the high points for my favorite languages in some later posts.

« Page 2 / 2