Four little plants

Upcoming project Plantr 2.0

11 September 2020

One of the first full stack applications I built was a gardening app. You could register, log in and the app would tell you how long your vegetables would have before they were ready to harvest.

My name for the app was originally "planter". But my partner said "lose the E. It's cleaner" in her best impression of Justin Timberlake/Sean Parker in the Social Network.

Of course Plantr has not exactly gained popularity of the website referred to in the title of the Social Network. That's okay. It served its purpose in teaching me a lot about building full stack applications. I'll go into some of the things I learnt:

What worked developing Plantr 1.0 #

I built Plantr with vanilla javascript and PHP. I used PHP's password hashing functionality to keep the passwords safe.

This password hashing is important, as generally people will use the same passwords for everything that needs a log in. So while it isn't too bad if someone hacks into a gardening app, the attacker might've also grabbed the password to your gmail and/or your bank's app. I used PHP's native password hashing function which uses bcrypt and it's own salting.

Essentially I was outsourcing this piece of security to someone a lot smarter than me. If you're just an application developer concentrating on developing a product, leave this kind of thing to the experts. Trying to roll your own security will undoubtedly end in tears. So I will continue to "stand on the shoulders of giants" (or software security experts) when building applications.

Plantr was a client side app with a simple PHP API on the back-end. I tried to keep the application simple by keeping most of the logic on the client side, and make my backend as dumb as possible. It would essentially just run CRUD (create, read, update delete) functions, and manage authentication. This emphasis on simplicity made reasoning about the application a lot easier and sped up my development time. Simplicity is something developers strive for and I'd really recommend Rich Hickey's talk on simplicity.

Also I had recently read "don't make me think" by Steve Kruger, which pushes developers/designers to optimise sites for usability. Using his philosophy I created a UI which was easy to use.

Watching out for password security, keeping a lot of logic on either back-end or front-end, and optimising for usability are all things that worked well building the application.

I would definitely use these techniques in future projects.

Now onto the uglier parts (please don't laugh too hard at some of these) :

What worked but was bad practice #

In Plantr I did a few things that didn't seem to completely ruin the functionality, but they are things that I would not do again.

Everything was "roll your own" #

The first thing was that I built everything in the app. The front end was completely vanilla javascript with zero npm libraries pulled in. The back-end pulled in one single library for emailing (PHPmailer).

This meant I wasted my time building a lot of functionality which definitely already exists out there in npm and composer. For instance, I spent a long time working out how to calculate the difference in days between two dates. Now I know of a great npm library called Day.JS. This would've been a lot quicker to pull in and hit the ground running.

This is why for Plantr 2.0 I'm going to build it as a server side rendered Laravel app (with a little client side javascript sprinkled in).

No database #

The next thing I did wrong was that I didn't use a database.

"WHAT? You built an app with authentication and didn't use a database?"

I know, it's bizarre. How and why did I do this? I wanted to concentrate on learning PHP and javascript and hooking up these two sides of the application. I didn't want to add a third variable/piece of technical debt to worry about. For this reason, I used CSV files - it was a little bit less of a jump for me to understand a flat file system than a database.

Next time I would definitely use a database for many reasons. Databases are designed for the use of web applications with security, concurrency and performance baked in. Also I wrote a lot of code to parse the CSV files into PHP objects. With the PDO statement API and a few SQL queries I could've grabbed this data easily by following conventions.

One other problem was that I had the filename holding user information baked into the source code. Thankfully, I never uploaded the source to Github, but if I had this would be BAD NEWS. An attacker might know the exact location of user data if they managed to hack the app.

With databases there is a tried and true convention of using a .env file ignored by git to store your database credentials which is a lot better.

What didn't work #

All Static classes #

That's right. I built the entire back-end application with 100% static classes.

As I said before, I was trying to keep the application as simple as possible. I was also getting interested into functional programming and the habit of getting rid of state and side effects. So I tried to use all static classes to get towards this ideal.

It didn't work.

If I really wanted to double down on functional programming, I should've been using good run-of-the-mill functions. These would be far easier and quicker to compose than static methods on a class. Using static classes I was also missing out on one of the most useful concepts in object oriented programming. That's right, I was missing out on DEPENDENCY INJECTION.

In my more recent programming experience I've "heard the good news" of dependency injection. The idea is you pass the class what it needs (usually in a constructor), you tell the class what it needs to do (in functions) then you grab from it what you need. And we don't have to worry at all about the implementation details throughout this process. It's all hidden away in the object itself.

An example of where I could've used this is in Plantr was when I was processing CSV files I had two different classes for the users table and the plants table. They both had a lot of the exact some CSV CRUD functionality copied and pasted in. If I had coded this properly I would've had a "CSVModel" class. This could have all the CRUD actions based in, and the application could simply pass the filename of the CSV file to the object constructor. That would mean being able reuse a whole lot of code.

The other benefit of dependency injection is it makes testing a lot easier. The tests can pass the object the data it needs, and test whether this data will produce the expected output.

This brings us to the next thing that didn't work:

No tests #

I did actually originally try to write Plantr with test driven development, but I gave up very quickly. And it cost me.

Testing takes some setup, but it will pay for itself in time. Parts of your application can break at unexpected times. Things go wrong. We are only humans and will definitely make mistakes. Half the time as developers, we will be console.logging, writing logs to files and manually testing to make sure our application is working anyway. Why not instead turn this into a test you can continually run from one command?

When building Plantr I would constantly break things. If I had written some tests in the program I would've know exactly where and when the breakages were happening.

Having no tests did not work.

Plantr 2.0 #

So I'm going to take what I learnt from this experience and re-build Plantr. This time as a laravel application hosted on heroku and here's what I'll do:

Let's see what I can learn this time. I'll post a link when it's complete!

Back to blog