Avoid Silent Failures

This is the sixth post in my series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

Rule #6: never fail silently. Report any unexpected condition with a log message.

Never Fail Silently

Mistrust

My coding style is ‘defensive’: trust nothing, verify all is well all the time.

Mistrust the data being passed into the function. Mistrust the data you read from a file. Mistrust the result you’ve just calculated. Mistrust that there is enough memory for you to allocate. Mistrust that the directory actually exists…

Mistrust leads to more robust code which can sensibly handle most anything you can throw at it.

Be A Snitch

The other side of the coin is that my code has to be communicative. Each time my code sees anything even slightly odd, it will report the oddity in a log message.

A fictitious JavaScript example:

let v = x.attr;

The code accesses a property on an object.

Now, what happens when x is undefined? The code will throw, possibly unrolling multiple levels of nested function calls, until the exception is caught or the script crashes.

More defensive coding might be something like this:

let v;
if ("undefined" != typeof x) {
  v = x.attr;
}

which may or may not be sensible; it depends on the context.

Main thing: this code defends itself against an undefined x, whether the variable itself is undefined or whether the variable contains undefined as a value. The value of v will remain undefined in both those cases.

Unless an undefined x is totally normal and expected, this code should also report the oddity to the developer, so the developer is alerted to the issue and can investigate.

let v;
if ("undefined" != typeof x) {
  v = x.attr;
}
else {
   LOG.warning(arguments, "x is unexpectedly undefined");
}

What To Report?

Much depends on the context.

If this odd condition for x is an outright error, I’d use LOG.error() or similar.

If this condition is strange, but not impossible I’d use LOG.warning() or similar.

If the condition is rare, but not unexpected, I’d use LOG.note() or similar.

And if the strange condition is not strange at all, and occurs all of the time in the normal code flow, I’d not add any logging.

Condition Ladders and Logging

I routinely use condition ladders in my code: a series of sanity checks. Nearly all functions climb down a ladder of tests and sanity checks before getting to the ‘meat’ of the function. I’ll elaborate further on condition ladders in a future post.

As my code descends down the rungs of the ladder, it might ‘fall off the ladder’ for expected or unexpected conditions.

For each unexpected condition, my code will emit a log message.

Sub importLicenseInfoFile(in_importFile as FolderItem)

  logEntry CurrentMethodName
  
  Do // While True: Non-loop for condition ladder
    
    try
      
      Dim appStorageDir as FolderItem
      appStorageDir = getAppStorageDir()
      
      // First rung of the condition ladder
      if appStorageDir = nil then 
        // Report unexpected condition
        logError CurrentMethodName, "appStorageDir inaccessible" 
        Exit // Fall off condition ladder
      end if
      
      Dim relativePathsToOmit as Dictionary
      relativePathsToOmit = new Dictionary
      relativePathsToOmit.Value(
        DIR_NAME_APP_STORAGE + SEPARATOR_ZIP_INTERNAL_PATH + DIR_NAME_PREFS
      ) = true
      
      Dim fileNamesToOmit as Dictionary
      fileNamesToOmit = new Dictionary
      fileNamesToOmit.Value(".DS_Store") = true
      
      // Second rung of the condition ladder
      if 
        not unzipUnwrappedFolder(
              in_importFile, 
              appStorageDir, 
              relativePathsToOmit, 
              fileNamesToOmit) 
      then
        // Report unexpected condition
        logError CurrentMethodName, "import failed" 
        Exit // Fall off condition ladder
      end if
      
      // 'Meat' of the function after we've climbed down the ladder
      // without 'falling off'.

      CMachine.singleton().async_verify(false)
      
      CCatalogEntry.scanCatalogEntryDir()
      CLocalAccount.scanAccountsDir()
      CProduct.scanProductsDir()
      COrder.scanOrdersDir()
      CGrant.scanGrantsDir(true)
      call CCapabilityWrapperForCustomer.getAllCapabilities()
      COrder.verifyAllCapabilities()
      
      clearAndRebuildWindows()
      
      // Log anything that might be of interest
      logNote CurrentMethodName, "import completed" 
      
    catch e as RuntimeException
      // Log any unexpected throw
      logError CurrentMethodName, "throws " + e.Message 
    end try
    
  Loop Until true // Non-loop for condition ladder
  
  logExit CurrentMethodName
  
End Sub

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Log Early, Log Often

This is the fifth post in my series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

Rule #5: add logging infrastructure. If you can add it from the get-go.

When I start on a new project, I make sure I immediately start adding logging infrastructure.

Most of the time, I will reuse a logging module from other projects – see further below for some sample code.

Why do I want logging?

As long as all is well, I don’t need logging.

Adding logging infrastructure to my code falls under the heading ‘defensive coding’. I assume that anything that can go wrong will go wrong.

The logging infrastructure I add to my code is my friend. I can ask it for help when things go wrong.

Rather than blindly having to try and diagnose a strange problem on someone else’s remote machine through trial and error, I’ll ask them to enable logging and nine times out of ten, the cause of the problem becomes immediately obvious.

When to add logging?

The key is to start adding logging statements to the code from the get-go, as early on in the development cycle as possible. I add logging as I write code, even in temporary or throwaway code.

Adding logging statements to existing code as an afterthought is a massive job, and it rarely gets fully completed. This then leaves me with ‘blind spots’ in areas of the code where I did not add the necessary logging calls.

What features are useful?

Levels Of Verbosity

A good logger has multiple levels of verbosity from ‘totally silent’ to ‘crazily chatty’.

In practice, I never actually use the ‘totally silent’ level. Even during normal operations I want to at least be informed of any unexpected errors that were encountered.

Output Redirection

Another feature of a good logger is redirection: I want to be able to redirect the logging output to multiple outlets.

While I am coding away, I could use straight console.log() or $.writeln() or similar and that might work fine during the development phase.

But when my code is running on a customer’s computer, I don’t have access to a debugging environment, and those calls to console.log() or $.writeln() will probably vanish into the bit bucket.

With a logger that can be redirected to one or more alternate outputs, I can still obtain logging output even if my code is running on a distant computer that I cannot see or control.

My logging code can output to the console, and/or to the system log, and/or to a dedicated log file on the user’s desktop and/or to a dialog that pops up on the users’ computer and/or…

Source Location

Ideally the logging output should also provide the name of the current function or method from which the logging call is made.

In some languages, that can only be achieved by manually adding the name of the current method to the message in the logging call.

function myFunction() {
    LOG::message("myFunction: starting function");
}

In languages like JavaScript, PHP, Xojo, C/C++… the name of the currently executing function or method can be dynamically calculated, which makes maintaining logging statements much easier: I can rename and restructure my functions without having to manually adjust all logging statements inside of the function.

Here’s a sample helper function I use in PHP:

   private static function logSourceLocation(): string
    {
        $backTrace = debug_backtrace(3)[2];

        $function = $backTrace['function'];
        $class = $backTrace['class'];
        $file = $backTrace['file'];
        $line = $backTrace['line'];

        $logSourceLocation = $file . ":" . $line . " ";
        if ($class)
        {
            $logSourceLocation .= $class;
        }
        $logSourceLocation .= $function . " ";

        return $logSourceLocation;
    }

Entry/Exit Hooks

Finally, I will routinely add a logging statement at the entry and exit of every method.

These calls remain ‘dormant’ 99% of the time. When things get really hairy, turning these statements ‘on’ allows me to trace the execution sequence and diagnose weird crashes when normal crash logs provide insufficient data.

For me, the LOG.entry() and LOG.exit() (or similar) calls also come in handy for debugging sessions, where I can break the debugger inside these calls depending on certain conditions, possibly deeply nested into the code.

Externally Configurable

The logging infrastructure needs to be externally reconfigurable. There needs to be some way to modify the logger settings without having to rebuild a ‘special version’.

There are many ways to do this: command-line arguments, settings in a config file, a special sentinel file… The logger can be mostly dormant, and when needed to the logger can be activated and redirected as needed.

What to log?

I will log anything out of the ordinary, and some ordinary stuff too.

Exceptions being thrown (level = ERROR).

Function parameters that are not within the expected range (level = ERROR).

Intermediate phases of a complex algorithm (level = NOTE).

Conditions that are probably OK, but should only rarely occur… (level = WARNING).

Entry and exit into any function or method (level = TRACE).

and so on…

In a production environment, I tend to run at level ‘error’ or level ‘warning’. That means that any logging call with a note will not make it into the logging output.

When problems occur, I will increase the level to ‘note’ or ‘trace’.

Sample Code

For Adobe Creative Cloud/CEP projects I can generate a starter project using CEPSparker, and the starter provides logging functions, for example:
https://github.com/zwettemaan/ScriptRunner/blob/aabed7739c80fba9d6f2c53fcb5c0e48392253f2/shared_js_jsx/utils.js#L71
or I can also use the CRDT_ES project:
https://github.com/zwettemaan/CRDT_ES/blob/cdb933fbfc2d51437c6f26c98581df911223215a/scripts/CreativeDeveloperTools_ES/crdtes.jsx#L409

For UXP I use the CRDT_UXP project:
https://github.com/zwettemaan/CRDT_UXP/blob/747befdd625d5f93f2daad88de9dda1db723d26d/CreativeDeveloperTools_UXP/crdtuxp.js#L888

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Any Coding Convention, But Pick One.

This is the fourth post in my series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

Rule #4: decide on a coding convention, ANY coding convention.

There is no best coding convention, but you have to have one.

Coding Conventions Are For Humans

Coding conventions are to software developers as style guides are to graphic designers.

When you are writing code, you are effectively communicating with two different audiences.

One audience are the computers who will be executing the code you wrote. This audience has no room for imagination: the code is the code, and that’s that. The target computer will perform whatever operations are requested from it.

The second audience are humans, who need to read your code. These human readers can be yourself, your colleagues, project managers, other coders… This audience has different needs than the first audience. They need to understand what, how, why, when, what not, what is related, what is similar,…

Coding conventions are all about making communication with your human audience more efficient. When reading code, the humans see the same code, but unlike the compiler or interpreter program, they don’t have immediate access to lookup tables for the whole program structure.

A compiler program ‘knows’ what the current scope is, whether a particular word refers to a local or global variable or a method name,… A human reader will not have a total recall or just might be skimming the code without having read and understood a whole preamble.

Coding conventions are a way to communicate with a human reader, help the human reader remember or understand things.

A coding convention can dictate things like:
– how to name various elements: methods, functions, macros, variables in certain scopes
– where to put various types of elements – at the top, in alphabetical order, in a separate file,…
– how to format the code: aligning things to help the human reader see structures
– how to split your code into separate files to keep it manageable
– how complex certain coding constructs can be
– how to handle logging and exceptions

The Best Coding Convention Is To Have One

There is no ‘best’ coding convention: there are many factors at play:
– what is the programming language? There might be existing ‘best practices’ for the language at hand, which might be a good starting point for a coding convention
– how large is the project? If the project is small and simple, there is less need for a coding convention.
– how large is the team? If there is only a team of one, it’s probably OK to not write down the coding convention, but it sure helps if you do. If there are multiple people on the team, it’s better to explicitly document things in written form.
– personal preferences. To each their own bracketing style.

In the end, the most important recommendation is: make sure you have a coding convention.

The second most important recommendation: be flexible and forgiving.

A coding convention is a means to an end. Some people will make adherence to a coding convention a goal rather than using it as a tool, at which point the coding convention starts becoming less useful.

If a coding convention costs you more than it’s worth, abandon it.

An example: for a while, I used comment lines with repeated asterisks as a visual indicator of comment areas. I stopped doing that: it has no real benefits and was soaking up valuable time.

I don’t follow my own conventions during exploratory coding. When testing ideas or prototyping, cleanliness comes second. But once things ‘gel’, I go back and clean up.

After each ‘arch’ of coding activity, which might span a few days, I’ll spend a few hours to refactor, restructure, comment, document the new code.

Personal Favorites

My current personal coding conventions include the following:

  • I try to hug the left margin. I keep individual lines short, and don’t indent very far – code should be viewable without needing text wrapping. Something like 80 or 132 characters per line. I’ll restructure code to stay within reasonable limits. This makes the code easier to read.
  • I use condition ladders to avoid deeply nested if statements.
SPRK.path.addTrailingSeparator = 
  function addTrailingSeparator(filePath, separator) {

    var retVal = filePath;
    
    SPRK.logEntry(arguments);

    do { // Start of the ladder

        // First rung: fall off the ladder if there is no filepath
        if (! filePath) { 
            break; // fall off the ladder            
        }

        // Second rung: fall off the ladder if the file path already 
        // ends in a separator
        var lastChar = filePath.substr(-1);        
        if (
            lastChar == SPRK.path.SEPARATOR 
        || 
            lastChar == SPRK.path.OTHER_PLATFORM_SEPARATOR
        ) {
            break; // fall off the ladder
        }

        // Bottom of the ladder - all is well, we can now 
        // perform the actual function
        if (! separator) {
            separator = SPRK.path.SEPARATOR;
        }

        retVal += separator;
    }
    while (false); // End of the ladder

    SPRK.logExit(arguments);

    return retVal;
}
  • I only use one single return statement per method. No early returns. This helps when debugging code: I can confidently put a breakpoint on the return at the end of a method, and be certain that it will be hit before the method returns.

    Instead of early return I use condition ladders, and ‘fall off the ladder’ instead of using an early return.
  • Never fail silently. Anytime even a slightly odd condition occurs, I’ll make a log entry.

    This works well with condition ladders: each the code falls off the ladder because of an unexpectedly unmet condition, I will emit a log entry.

    I will re-use any good existing logging infrastructure, or add a new logging module, where logging can be controlled by severity level, can be redirected to the console, to specific files, to system logs…
  • Spaces. I use a lot of spaces to format things so their structure is highlighted.

    Complex expressions and statements are laid out in a vertical fashion, so the reader’s eye can more easily separate the subexpressions and substatements, spot the patterns in naming and structures,…
...
SPRK.C.LOG_NONE                                 = 0;
SPRK.C.LOG_ERROR                                = 1;
SPRK.C.LOG_WARN                                 = 2;
SPRK.C.LOG_NOTE                                 = 3;
SPRK.C.LOG_TRACE                                = 4;

SPRK.C.APP_CODE_AFTER_EFFECTS                   = "AEFT";
SPRK.C.APP_CODE_BRIDGE                          = "KBRG";
SPRK.C.APP_CODE_DREAMWEAVER                     = "DRWV";
SPRK.C.APP_CODE_FLASH_PRO                       = "FLPR";
SPRK.C.APP_CODE_ILLUSTRATOR                     = "ILST";
SPRK.C.APP_CODE_INCOPY                          = "AICY";
SPRK.C.APP_CODE_INDESIGN                        = "IDSN";
SPRK.C.APP_CODE_PHOTOSHOP                       = "PHXS";
SPRK.C.APP_CODE_PHOTOSHOP_OLD                   = "PHSP";
SPRK.C.APP_CODE_PRELUDE                         = "PRLD";
SPRK.C.APP_CODE_PREMIERE_PRO                    = "PPRO";
...
...
    if (
        Dirs::getLowerCaseFileNameExtension(entryFileName) != FILENAME_EXTENSION_JSON_LOWERCASE
    ||
        entryFileName.length() != ENTRY_FILE_NAME_LENGTH
    )
    {
        LocalPacket::deletePacketFile(entryPath);
        FUNCTION_BREAK;
    }
... 
  • I often alphabetize. E.g. I keep methods in a source code file in alphabetical order. While coding within a helpful modern IDE that’s not necessary nor useful, but when debugging with a primitive debugger, I find it helps in navigating a source code file during debugging because I know what methods are ‘before this’ or ‘after this’.

    With constants, keeping them alphabetized helps me spotting irregular names for constants, or occurrences of duplications with similar names.
  • I try to name literal constants in the code.

    Nearly all strings and numbers are ‘elevated’ to named constants, and I use predictable patterns for picking the names of the constants.

    This has a number of benefits:
    – it allows the compiler or interpreter to catch some typos
    – it allows me to search the source code for specific constants. E.g. if I have
const FILE_NAME_EXTENSION_TXTFILE = "text";
const TAG_NAME_TEXT               = "text";
...
let fileName = fileNameRoot + "." + FILE_NAME_EXTENSION_TXTFILE;
...
let fullTag = 
  "<" + TAG_NAME_TEXT + ">" + 
    tagContent + 
  "</" + TAG_NAME_TEXT + ">";

I can search for either FILE_NAME_EXTENSION_TXTFILE or TAG_NAME_TEXT and only ‘hit’ the code I am interested in.

Without named constants, I’d search for the literal string ‘text’ and would find a mix of both types of occurrences.

This often occurs when working with databases, where multiple tables contain same-name columns. With named constants, I can do targeted source-code searches for, say, FIELD_NAME_DESCRIPTION_PRODUCTCODE vs. FIELD_NAME_PRICE_PRODUCTCODE, which might be evaluate to identical string literals "ProductCode", but refer to same-name columns in different tables.

  • Instrument all methods with calls to an entry and exit log function. Most of the time, these entry/exit calls are do-nothings or ‘nearly’ do-nothings.

    In C/C++ I’ll use macros for these, so they can ‘pooff!’ away in production code.

    Yet, when the proverbial hits the fan and the code crashes on a customer’s secret server to which I don’t have access, I can (selectively) enable these and the code will produce log files that can help me find the location of the crash.

    In many languages, the language environment has facilities to ‘inject’ this kind of entry/exit code dynamically, without needing to explicitly adding these logging calls, but I work in a lot of environment where such dynamic code injection is not available, so I stick with the ‘hard coded’ entry/exit calls in all of my functions.

    Another advantage is that I can use these to determine code coverage (find out about unused sections of code), and I can use them to gain some rough performance measurements and identify CPU hogs.

    Finally, when debugging, these calls give me a foothold to insert some specific debugging/testing code which allows me to break into the debugger when a very specific condition occurs (e.g. something deeply nested in a complex execution).
  • In my code, exceptions are considered exceptional.

    I will try hard to avoid any throwing. This means that I will test preconditions beforehand and fall off condition ladders, to make sure the code will not throw.

    Then, if the code unexpectedly or unavoidably still throws, I will always catch exceptions in the method where they occur, and at least make a log entry.

    If the calling code expects to catch exceptions for special conditions, I’ll re-throw, but generally speaking, I prefer to return all info by way of a return value, or a passed-in call-by-reference variable.
  • I don’t like very short index variables like i, j, idx,… – I rather name them so their name reflect what they are indexing – pageIdx, colorIdx,….
  • If a method or function has too much nesting, it’s a sign it needs to be broken up in a sensible way. I’ll avoid nested loops.

I’ll discuss all of this in more detail in the upcoming blog posts in this series.

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Contributing To An Existing Project? Understand It Before You Fix it.

Don’t sweat the details.

This is the third post in my series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

If it’s not your own code you’re working on, try to fit in and accept existing code conventions.

I don’t know about you, but I often have to consciously fight the ‘Not Invented Here’ syndrome.

As exemplified by this series of blog posts, I have some strong opinions on ‘how to do it right’. Strong opinions are fine – as long as I apply them to code that I own.

As soon as I am working on someone else’s project, the first priority is to understand the coding conventions.

In some cases, there might be some documentation on the coding conventions. It might or might not help, because sometimes the documentation is long outdated.

There might be no formal documentation and I just have to discover the conventions. If no documentation can be found, I’ll try to understand what they are through osmosis.

Read the code. Step through the code. Use project-wide ‘Find’ and the features of my IDE to discover the hidden relations.

Create a temporary separate source code repository branch and use it to dissect the code. Resist the urge to start refactoring too early.

Sometimes there are or have been multiple contributors to the project and they each brought their own conventions. Sometimes these people have moved on, but their coding conventions remained behind, and no-one remembers ‘why?’

Once it’s time to contribute, I will do my best to match the existing conventions, even if they don’t match my personal preferences.

If conventions are missing or counter-productive, I’ll start a discussion with other stakeholders.

What not to argue about:

  • Spaces vs. tabs.
  • Size of indents.
  • Positioning of braces.
  • Capitalization
  • Underscores and $ in variable names

We all have a personal preference, but these are not worth any time or social capital. Just stick with the project’s preference.

However, if there is no convention at all, it it’s all inconsistent, I might make an exception and ask for advice.

My goal is to write new code that ‘fits in’ – i.e. it looks at home within the project, and does not stick out like a sore thumb.

What is worth discussing:

  • Obvious problems (e.g. returning a pointer to a stack variable in C/C++, one-past-the-post bugs…)
  • Make the code more defensive
  • Logging anything out of the ordinary
  • Meaningful error messages

Make your code blend in. It’s important to respect the work that came before.

Related

https://thedailywtf.com/articles/the-inner-json-effect

As read by the Primeagen:

https://www.youtube.com/watch?v=KmwI_qnLSOg

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Write Code That Is Human-First

This is the second post in my series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

Rule #2: write your code so a smart 12-year old with no programming experience can follow the general idea.

You’ll be so glad when you need to revisit that code in a few years time.

Advanced Language Constructs

CPU cycles are cheap, human attention span is expensive. In my book, trading CPU cycles for code that is easier to read and understand is a good trade.

Many languages and frameworks provide clever ways to express complicated concepts in just a few characters. If you live and breathe that environment, you can express a complex operation with very little code. That gives you a short-term ‘win’.

Now picture the situation where someone who is not as familiar with the advanced language constructs needs to understand and maybe adjust the code.

That someone might be someone new on the team.

Or it might be you, six months or a year later. By now, you’ve switched to a new framework, and you might have forgotten the more intricate details of the previous framework. Frameworks and language features come into and go out of fashion at a rapid pace.

Or I might be showing my code to someone who knows how to code, but does not know ‘my’ language.

Or maybe I am on holiday and the project manager, who does not speak the language fluently wants to try and check on how some process works.

It is to my benefit if they still can ‘grok’ the gist of what the code does, rather than get mired into weird and wonderful constructs.

The way I tackle this is to try and avoid advanced language or framework constructs if I can.

If the benefit of the construct is mostly in conciseness of expression, rather than an extreme performance increase, I’ll avoid using it.

If I do use something fancy, I will document it clearly in the README so future-me doesn’t have to reverse-engineer it.

Code Like A Graphics Designer

Just like in graphic design and publishing, white space is a crucial tool for guiding the reader’s eye. Code readability can be increased tremendously with thoughtful use of empty space.

When laying out a flyer or article, designers don’t cram every inch with content; they use spacing to make structure visually intuitive.

I apply the same principle in my code: I use newlines, indentation, and vertical layout to reveal the structure of complex expressions.

It’s not about saving lines, it’s about making logic visible.

Small Steps

When coding I see my code as a whole sequence of ‘thoughts’.

I have no formal definition of what a ‘thought’ is but as I read through code, my brain will step through these thoughts.

If the thoughts in the sequence are simple, the code is easy to follow.

If a thought forces the reader to mentally solve a Rubik’s Cube, the code is too complex.

I try to spread out thoughts and make sure there is room to ‘breathe’. The mind of the reader needs to digest what it has seen and get ready for the next bit.

By breaking up the code into small ‘thoughts’ and introduce them gradually, rather than ‘clump’ it all up in a dense pile of clever code, I try to make the meaning clear.

I only worry about performance after all is said and done: premature optimization is the root of much evil. When writing code, I will not worry about performance – I only worry about making the code readable. After all, in my experience, choosing the right algorithms is much more effective for getting good performance than writing dense, high-functioning code.

I will often introduce ‘one-time-use’ functions to make thoughts clearer, by putting a name to them.

For example, grabbing a file name extension from a file name or path can easily be written out explicitly, but even so, I’ll still wrap such expression into a function, say getFileNameExtension(theFilePath) or so and make it easier to grok the code.

Sometimes I work with the concept of ‘stories’: I don’t adhere to the idea that functions should always be short or have a proscribed maximum length.

Some of my functions might span multiple pages. These I see as ‘story functions’ – they tell a story, and artificially breaking them up might disrupt the story and makes them hard to follow. Stories tend to go like ‘this and then that, and then that, and then that…’, i.e. a nearly straight sequence of calls into other functions.

My code will be mostly short functions (one thought per function), and might also have a few long functions (one story per function).

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Leave The Code A Little Cleaner Than You Found It

This is the first in a series of posts of a series: Coding Without the Jargon, where I show how I clean up and gradually improve real-world code.

Rule #1: every time you’re working on a body of code, try to improve the existing code a little bit.

Gradual improvements can be more effective and less disruptive than going ‘full refactor’.

Things I will often do:

Add Functions or Methods With Meaningful Names Instead of Comments

An example: there might be repeated code like (PHP sample):

$timestamp = round(microtime(true) * 1000);

You might be inclined to add a comment:

// Get current time since epoch in milliseconds
$timestamp = round(microtime(true) * 1000);

But I would not do that.

Instead, I prefer using a function like currentTimeSinceEpochInMilliseconds().

That way, the code can become ‘self-commenting’.

function currentTimeSinceEpochInMilliseconds() {
    return round(microtime(true) * 1000);
}
...
$timestamp = currentTimeSinceEpochInMilliseconds();

Before I’d dive in, I’d make sure a similar function does not already exist in the code.

Using global find, and regular expressions, performed over the whole project source code I’d search for clues like timestamp, microtime, epoch, current

Remove Useless Comments

Some comments can make the code harder to read and harder to maintain. They clutter the code and can be misleading.

Read the code, look for such comments and remove them.

One type of less-than-useful comments is ‘stating the obvious’.

// Here we increment the customerCount by 1
customerCount++
;

This comment adds nothing of value, and it might become a lie in the future – say, someone comes in and makes the increment a variable but does not notice the comment:

// Here we increment the customerCount by 1
customerCount += customerBatchSize;

If the person making the change does not notice the comment and removes it or adjusts it, then the comment becomes a lie.

Space Out Complex Expressions

Spend some time reformatting complicated expressions, merely adding white space.

The computer does not care, but the human reader greatly cares. In most programming languages, you can spread complex expressions over multiple lines, and use spacing to draw the human reader’s attention to subexpressions and scopes.

Simple example

            while (! isTimedOut(tryToFinishBefore) && (fIsRunningServicesTimeslice || ! exitEventLoopWhenTrue())) {
...
            }

can become

            while (
                ! isTimedOut(tryToFinishBefore)
            &&
                (
                    fIsRunningServicesTimeslice
                ||
                    ! exitEventLoopWhenTrue()
                )
            ) {
...
            }

Add Comments

I like to avoid comments by making the code be self-documenting if I can, but that is not to say comments are not useful.

While you’re debugging or adding to the code, you might need some time figuring out some existing complicated code.

At some point in the future, you, or someone else, might end up back in the same area, wondering what it all means. Why not save that person some time, and record your findings?

I try to initial and date my comments: when reading comments, knowing who made them and when they made them is often very useful info.

// KC 20250321 This code attempts to calculate a unique ID by 
// hashing the data, but it needs a bit of work. It does not 
// handle the edge case where the string is empty - TODO
...

By marking up my comments, I can do global searches for // KC and hit all my comments and review them. Or during the next refactor I can search for TODO and find all the spots that need work.

Remove Unused Code

Depending on the project and programming environment at hand, it might be easy to identify unused code.

Commented out code is a prime candidate for removal, of course. It needlessly clutters the code and hinders a global ‘find’ over the whole project because it will generate false positives.

But there might also be functions or named constants that are never called or referenced.

An example: at times I find myself writing additional functions ‘just in case I might need them’, because they’re pretty much ‘for free’ and come with very little extra effort. And occasionally they do come in handy: I often have a hunch of what I might need. But if I don’t need them after all, I’ll eventually strip them away.

There are environments where functions are not necessarily referenced by name, in which case it might be harder to determine use of a function, but in many environments, a simple global search can be enough to determine whether a function is unused.

Remove it, and there’s less code to maintain. If need be, you can always bring it back from your source code control system.

Add Tests

You can always make the code more and more self-testing – and you don’t need an existing framework to do this.

All you need to do is add code that tries out various functions and compares actual results with expected results. Any kind of sensible test will help – it does not matter if the tests are not (yet) comprehensive.

Writing tests will help you think about edge cases: you’ll start wondering things like ‘what if this string is empty?’, ‘what if the search string is embedded twice?’,…

These tests can be triggered in all kinds of ways – one-shot triggers, only in debug versions, only when certain .env variables are set. You can pick whatever method works best for you.

These tests act as a canary in the coal mine: some time in the future someone might change something. These tests might be the one thing that saves your bacon as they suddenly start failing.

And finally: such tests are very useful as sample code. Want to understand out how a function works? Look at the tests.


    SANITY_CHECK(Utils::intToHexString(0,0) == "", FUNCTION_BREAK);
    SANITY_CHECK(Utils::intToHexString(1,4) == "0001", FUNCTION_BREAK);
    SANITY_CHECK(Utils::intToHexString(10,6) == "00000a", FUNCTION_BREAK);
    SANITY_CHECK(Utils::intToHexString(0x12345678,8) == "12345678", FUNCTION_BREAK);
    SANITY_CHECK(Utils::intToHexString(0x9ABCDEF0,8) == "9abcdef0", FUNCTION_BREAK);

    SANITY_CHECK(Utils::isWildCardMatch("", ""), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("a", ""), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("", "a"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("", "?"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("", "*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "a"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "a*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "*a"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "*a*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "*a**"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("ab", "*a**b"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("ab", "a*b"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("acb", "a*b"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("acb", "a*c"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("a", "?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("a", "a?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("a", "?a"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("ba", "?a"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("a", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("ba", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("ab", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("bca", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("abc", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("aac", "?a?"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("ax", "*a?*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("dsdsaap", "*a?*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("dsdsaadsdhsja", "*a?*"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("dsdsdsdhsja", "*a?*"), FUNCTION_BREAK);
    SANITY_CHECK(Utils::isWildCardMatch("baxss", "*a?*"), FUNCTION_BREAK);
    SANITY_CHECK(! Utils::isWildCardMatch("baxss", "a?*"), FUNCTION_BREAK);
...

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Coding Without the Jargon

As part of my day-job I review, debug and fix a lot of code written by other people.

At the same time, I am tracking various influential people; there are a lot of opinions and ‘best ways’ to do things. Patterns, Continuous Development, Dependencies, Injection, Observers and Subscribers… lots of words with ‘deep’ meanings.

Make no mistake: those patterns and principles are very useful, but their meaning is often obscured by jargon and big words.

In practice, I see very little of those ‘best ways’ in the real-life code I am working on.

This is code, written by real people with real problems, and often there seems to be no time for the ‘big word’ principles.

Given an existing code base, it often seems impossible to go from ‘here’ to ‘there’ so we all just stay ‘here’.

Pragmatism

I’ve been coding for over four decades, in all kinds of languages, and gone through many phases.

In the last 10, 15 years my coding style has kind of ‘gelled’ into a number of ‘common sense’ principles which I use independent of the language I am coding in – be it JavaScript, C++, PHP, Python, bash, PowerShell, Xojo, COBOL…

More often than not, my common sense principles are built on various patterns and principles, but they are more easily explained in plain English.

My principles also allow me to do ‘gradual improvement’: I can very gradually massage existing code to adhere more and more to my principles, and make it more robust, stable and easier to debug.

What I intend to do is start writing a number of blog posts, each one elaborating on one of the principles I use in day-to-day coding and debugging.

As I write out post-by-post, the titles below will be converted into links that lead to the corresponding blog post – so come back and check regularly for updates!

The Principles

Stay Tuned

By adhering to and applying these principles, I have been able to successfully tame big, messy, problemsome code bases without massive rewrites.

I’ll start writing a blog post for each of the above principles over the course of the coming weeks…

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, plugins and plug-ins, large and small, to speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Conditional Code Without Preprocessor

There’s normally a performance price to pay for adding diagnostic code to programs written in languages that don’t have macros and conditional compilation like C/C++.

For example in JavaScript, consider something like:

LOG_DEBUG("Current Structure = " + structure.toString());

If this were C++, the LOG_DEBUG could simply ‘pooff!’ away in a release version simply by redefining the LOG_DEBUG macro as ‘nothing’.

But in languages like JavaScript, even though LOG_DEBUG might be stubbed out into a ‘do nothing’ function call, you’re still executing the toString() method and paying an overhead for calling and returning from LOG_DEBUG.

I have a little trick. To make it work, you need some coding discipline, but the trick allows me to enable/disable code in a script or program without actually removing the code.

The trick is to make sure the conditional bit of code fits on a single line.

Then prefix the code with a specially crafted comment, for example: /*EXTRA_LOGGING//*/

/*EXTRA_LOGGING//*/ LOG_DEBUG("Current Structure = " + structure.toString());

You can use different strings to identify different ‘types’ of conditional code – XX1, DEBUG, EXTRA_LOGGING,…

Then to disable all the EXTRA_LOGGING you simply do a global find-and-replace:

/*EXTRA_LOGGING//*/ -becomes-> /*EXTRA_LOGGING*///

and this comments out all the EXTRA_LOGGING lines.

Do the reverse find-and-replace, and they’re enabled again. It’s crude and a little fiddly, and you better not ‘break’ the lines, but it’s better than nothing.

Of course, you need to make sure the pattern does not occur in the source code except for these kinds of lines.

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, large and small, that can speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

Creative Cloud Desktop sez: You Don’t Have Access To Manage Apps

I’ve created two scripts (Mac/Windows) that might fix the issue of getting a message “You don’t have access to manage apps” in the Creative Cloud Desktop App.

You can find them here:

https://github.com/zwettemaan/FixAdobeAppsPanel/blob/main/README.md

What’s going on?

There are several valid reasons why the Creative Cloud Desktop app might block your access to your Adobe apps.

You might be part of a team and the admin has revoked your access.

Or you might be logged in to an incorrect Adobe account. Or something…

But each time I have personally encountered this, there was no clear, valid reason. One day it worked; the next day, I no longer had access.

If you search online, you’ll likely bump into this community forum post:

https://community.adobe.com/t5/download-install-discussions/quot-you-don-t-have-access-to-manage-apps-quot/td-p/14583430

which suggests a fix: modifying a file called serviceconfig.xml and restart the computer. It works.

However, the process is fiddly, and I got tired of going through the motions. I decided to automate the process.

Below, two scripts, one for Mac, one for Windows, which automate the process outlined in the community forum post.

I used the Claude chatbot to generate an initial version, then refined it manually to fix some issues.

Scripts On Github

Caveat: these scripts come with no warranty, express or implied. They work for me, but use them at your own risk.

I recommend inspecting the scripts first to ensure they will work for you. They’re simple and easy to follow.

Future changes to the location or format of serviceconfig.xml may break these scripts.

To run the scripts you need a user account that has administrative privileges.

If your computer is controlled by the IT department, you’re probably out of luck – you need to ask them to help you out.

Go here:

https://github.com/zwettemaan/FixAdobeAppsPanel/blob/main/README.md

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, large and small, that can speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan

IPv6-Related Connection Failures

(Added Note: the issue affects more than command line tools like npm. My Creative Cloud App on Windows started misbehaving and experiencing connection errors, and disabling IPv6 on the adapter level in Windows fixed it).

I ran into a little head scratcher today and I am documenting it here for future reference.

I wanted to pull and initialize a Node.js project in a small local virtual machine, and I tried to run

npm install

and nothing worked. I distinctly remember this used to work, but not this time around.

The symptoms were similar to a networking issue. But the network was up and running, I could ping and connect to various servers.

I eventually figured it out: the root cause is that IPv6 is not yet universally supported.

I live in New Zealand, and cannot connect to external sites using IPv6 – my ISP does not support it.

I do have IPv6 support on the office network, but that’s only for connections within my local network.

When performing a DNS lookup for registry.npmjs.org, I got a bunch of addresses – some IPv6, some IPv4.

What was happening was that the virtual machine was trying to connect to one of the IPv6 addresses for registry.npmjs.org, and it failed.

The workaround I used was to ‘turn off’ IPv6 in my virtual machine, forcing npm to connect to one of the IPv4 alternatives.

Exactly how you turn off IPv6 depends on the Linux distro and version. In my case, the virtual machine is running a recent Ubuntu, and I added the following lines to /etc/sysctl.conf:

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

and then

sudo sysctl -p

More IPv6 Clashes

I think I had similar issues in the past when trying to connect to GitLab.

And I just bumped into the same issue using the Creative Cloud App on Windows. It was claiming network issues, despite the network being up and running. Disabling IPv6 on the adapter level in Windows fixed that.

I hope publishing this gotcha here will maybe save someone else some time.

Next

If you’re interested in automating part of a Creative Cloud-based workflow, please reach out to [email protected] . We create custom scripts, large and small, that can speed up and take the dread out of repetitive tasks.

If you find this post to be helpful, make sure to give me a positive reaction on LinkedIn! I don’t use any other social media platforms. My LinkedIn account is here:

https://www.linkedin.com/in/kristiaan