ARCHIVE NOTICE

My website can still be found at industrialcuriosity.com, but I have not been posting on this blog as I've been primarily focused on therightstuff.medium.com - please head over there and take a look!
Showing posts with label readability. Show all posts
Showing posts with label readability. Show all posts

Saturday, 24 October 2020

Reading and writing regular expressions for sane people

Your regular expressions need love. Reviewers and future maintainers of your regular expressions need even more.

No matter how well you've mastered regex, regex is regex and is not designed with human-readability in mind. No matter how clear and obvious you think your regex is, in most cases it will be maintained by a developer who a) is not you and b) lacks context. Many years ago I developed a simple method for sanity checking regex with comments, and I'm constantly finding myself demonstrating its utility to new people.

There are some great guides out there, like this one, but what I'm proposing takes things a step or two further. It may take a minute or two of your time, but it almost invariably saves a lot more than it costs. I'm not even discussing flagrant abuse or performance considerations.


Traditional regex: the do-it-yourself pattern


The condescending regex. Here you're left to your own devices. Thoughts and prayers.

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

(example taken from this question)


Kind regex: intention explained


It's the least you can do! A short line explaining what you're matching with an example or two (or three).

// parse a url, only capture the host part
// eg. protocol://host
//     protocol://host:port/path?querystring#anchor
//     host
//     host/path
//     host:port/path
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;


Careful regex: a human-readable breakdown


Here we ensure that each element of the regex pattern, no matter how simple, is explained in a way that makes it easy to verify that it's doing what we think it's doing and can modify it safely. If you're not an expert with regex, I recommend using one of the many available tools such as regexr.com.

// parse a url, only capture the host part
//     (?:([A-Za-z]+):)?
//         protocol - an optional alphabetic protocol followed by a colon
//     (\/{0,3})(0-9.\-A-Za-z]+)
//         host - 0-3 forward slashes followed by alphanumeric characters
//     (?::(\d+))?
//         port - an optional colon and a sequence of digits
//     (?:\/([^?#]*))?
//         path - an optional forward slash followed by any number of
//         characters not including ? or #
//     (?:\?([^#]*))?
//         query string - an optional ? followed by any number of
//         characters not including #
//     (?:#(.*))?
//         anchor - an optional # followed by any number of characters
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Now that we've taken the time to break this down, we can identify the intention behind the patterns and ask better questions: why is the host the only matched group? Was this tested? (Because (0-9.\-A-Za-z] is clearly an error, and there are almost no restrictions on invalid characters)

Unless you're a sadist (or a masochist), this is definitely a better way to operate: be careful, and if you can't be careful then at least be kind.

Thursday, 23 July 2015

Setting up Squiz Labs code sniffer for PHPStorm on Windows

PHPStorm is a brilliant piece of software that's highly extensible. One of the extensions that's particularly useful is Squiz Labs' PHP Code Sniffer. It wasn't quite apparent from other documentation I found online that it's really simple to set up for Windows! So with the help of a co-worker I put together the following tutorial:

To install the code sniffer for PHPStorm:

  • Download and install PHP for Windows
  • Download https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
  • Create a file called phpcs.bat in the C:\php directory
    @echo off
    "C:\php\php.exe" -f "C:\PATH_TO_DOWNLOADED_PHAR\phpcs.phar" -- %*
  • In PHPStorm
    • Open File->Settings
    • Select Languages & Frameworks->PHP
      • Add a local interpreter that points to C:\php\php.exe
      • Select Languages & Frameworks->PHP->Code Sniffer
      • Update the local configuration to point to C:\php\phpcs.bat
        • increase the Tool process timeout if you're working on remote code
    • Click Validate to ensure that the code sniffer is loaded correctly
    • Click Apply
    • Select Editor->Inspections
      • Select the checkbox for PHP Code Sniffer validation
      • Select PHP Code Sniffer validation
      • Set the coding standard (you'll probably want PSR2)
    • To run the code sniffer
      • Press CTRL + ALT + SHIFT + I
      • Type "code sniffer" and press enter
      • Select the current file
And there you go!

Monday, 29 July 2013

Comment Driven Coding

This is undoubtedly my favourite trick, and oddly enough it's the one that drives experienced hackers round the bend!

Background

I've always thought that I had this commenting thing down. When my teachers in high school and in university told me I needed to comment my code, I listened! They were satisfied, too. I took up tutoring whenever I couldn't find work, and I adamantly preached what I practised. Nobody was going to be able to call me an inconsiderate coder.

That is, until I entered the army. My initial exposure to the comment style in an environment with high developer turnover and long-term maintenance responsibilities was like being picked up by a ship after days stranded at sea. For the first time I really understood the value of a well-placed, meaningful comment. I learned to expect comments whenever the code became even a little tricky, and I learned to be safe rather than sorry.

After a couple of years in the army, I transferred to the real deal: real-time avionics development. If the army comments were like a ship, this was like finding myself on dry land! I was shocked by the extreme attitude, and it took me a while to get to grips with it. For the first time in my life, I was writing comments for every single statement.

Every... single... statement. Let that sink in a moment. Even though each comment had to be meaningful - so instead of "add 1 to i" it would be "increment the loop counter" - this meant that there was far more comment text than code in any given source file. At first it was a bit of a headache, but soon the comments and the code began to blend and I found myself feeling far more comfortable with these complex systems than I'd ever been with any others. Nothing was left to the imagination, no guessing required. The things that didn't make any sense? They'd let you know precisely why.

After years of this I returned to the real world, the perpetually rushed, highly-stressed and highly caffeinated world of 25-hour work days led by marketing teams who promise the impossible to be delivered yesterday using codebases and frameworks that were built exclusively for the most antisocial misogynistic masochist enthusiasts and are generally worked on by whoever has the highest grades in the only-partially-related academic field of computer science.

These people do not have time for comments. They usually have a very specific idea of what comments look like, those unhelpful things that tell you what the code does. There's a popular opinion that good code is so readable that it doesn't need to be commented! But unfortunately, that's only partially true.

You see, and here I'm going to wind down on the personal history and get to the point of this post, good, clean code might tell you what it does but it doesn't tell you what it's supposed to do. It doesn't expose the logic of a group of statements, and it certainly doesn't provide an unambiguous guide to the code you're writing under the gun.

Method

Comment Driven Coding is summarized in four easy steps:
1. Comment before you code
2. Comment the desired behaviour in English
3. Split your comments into logical steps.
4. Repeat until your comments describe the smallest reasonable description of logical behaviour.
One level above pseudo-code, use comments as a way to structure your code prior to implementation. This will handle all of the logic before you let syntax and details get in the way. Begin with the higher level functionality and drill-down until you've fleshed out the details.

[EXAMPLE FUNCTION INSPIRED BY THIS POST]

/* Reduced Sum Of Digits (RSOD) is calculated by repeatedly summing the digits of a number until a single digit is produced. */
function ReduceToRSOD(number) {
  // return the RSOD
} // ReduceToRSOD

The first things to notice here are that we have explained RSOD clearly, we have explained the inner logic in the broadest sense possible and we have used a comment to explicitly mark the function of the closing brace. This last step may seem extreme, but as the number of braces and the size and complexity of the code increase it will become more difficult to keep track of the code blocks no matter how well the code is indented.

/* Reduced Sum Of Digits (RSOD) is calculated by repeatedly summing the digits of a number until a single digit is produced. */
function ReduceToRSOD(number) {
  // if the number is comprised of a single digit, return it
  // if the number is comprised of multiple digits
    // separate the digits and add them together
    // recursively apply the ReduceToRSOD function
} // ReduceToRSOD

By doing this using comments instead of code, logical errors or missed cases will be far easier to spot.

/* Reduced Sum Of Digits (RSOD) is calculated by repeatedly summing the digits of a number until a single digit is produced. */
function ReduceToRSOD(number) {
  // return an error code if the number is a not positive integer
  // if the number is comprised of a single digit, return it
  // if the number is comprised of multiple digits
    // separate the digits and add them together
    // while the number has more digits
      // add the last digit to a result variable
      // remove the last digit from the number
    // recursively apply the ReduceToRSOD function
} // ReduceToRSOD

The comments not only make the code readable, but they also function as the perfect tool for performing a code review. Does the comment logic make sense? Does the code do what the comments describe?
var INVALID_NUMBER = -1;

/* Reduced Sum Of Digits (RSOD) is calculated by repeatedly summing the digits of a number until a single digit is produced. */
function ReduceToRSOD(number) {
  // return an error code if the number is a not positive integer
  if ((parseInt(number)== Number.Nan) || (number < 0)) return INVALID_NUMBER;
  // if the number is comprised of a single digit, return it
  if (number < 10) return number;
  // if the number is comprised of multiple digits
    // separate the digits and add them together
    var result = 0;
    // while the number has more digits
    while (number > 0) {
      // add the last digit to a result variable
      result += number % 10;
      // remove the last digit from the number
      number = (number - (number % 10)) / 10;
    } // while the number has more digits
    // recursively apply the ReduceToRSOD function
    return ReduceToRSOD(result);
} // ReduceToRSOD
 
The reason this works so well is that even the brightest and most experienced programmers don't truly think in code. Our brains are wired for language and the easiest way to look at a problem is when it's expressed in words or images.

A well-written comment never gets old. The code might change, but what it's supposed to be doing will not.
 
At first glance it may look and sound like it'll take longer to write your code this way, but it really won't. It's quicker to write in natural language, and once the ideas have been broken down into their simplest logical components the code practically writes itself!

Labels