Skip to main content

the avatar of Martin de Boer

Updating the website

Update 03-04-2020

The website Fossadventures is now powered by the free Nisarg theme. Cannyon has served me well, but the problem with the premium theme is that it doesn’t automatically update. The Nisarg theme has many similar features and is free. It is also much faster to load. And it works better for mobile phones.

Original article

The website Fossadventures.com features a new look. After a successful start of the website, it was time to make it look even better! The website was using the free Cannyon theme. The updated website is now powered by Cannyon Premium. Updating the theme was not as easy as this might appear from the marketing materials: “Premium version is full compatible with free version. If you switch the free theme to premium you don’t lose data or options”. I expected some kind of ‘pro license key’ that would unlock additional features in the installed theme.

Instead, I got a download link to a ZIP file that contains the premium theme. To get this zip file onto my WordPress instance, I needed to increase the maximum upload size from 2 MB to 6MB. This required me to adjust the values in the PHP.ini file. Tutorials are plenty on the internet. However, my first few attempts didn’t work so well. I ended up searching all PHP.ini files on my openSUSE server and adjusting the values in all files. This resolved my first obstacle.

Next I moved the zip file to the correct location (wp-content/themes/) on the server and installed the theme. This was when I discovered that I needed to reconfigure all of my settings. And that the theme has 2 places for customization. There is a part under the Appearance –> Customize section. And there is a specialized part under the myTheme.es section.

After a bit of work, the site now features an updated look. The landing page shows featured images per blog post. And instead of viewing the full text, it only shows an boxed preview of the text. This makes it easier to scroll through the latest posts.

The hard work is not done yet. After checking Google PageSpeed Insights, it appears that this Premium theme is much slower than the Free one. Some CSS stylesheets are very large in size! My first reaction was to look at WordPress plugins that could optimize the website speed. I am now experimenting with:

Full use of Autoptimize removes all CSS and makes the website look like an early 90’s website (only text, no layout, no pictures). Enabling only the HTML and the JavaScript optimizations, but leaving out the CSS optimizations brings the site back to an acceptable state. These 2 optimizations do improve the page speed. So this is a gain at least.

Asset CleanUp makes it possible to prevent certain CSS sheets from loading. However, this plugin will also break the site. So for me personally, this is a plugin I will likely remove.

Through all this experimentation, I have learned that I still need a very small part of the stylesheets, when Google suggests to “Defer unused CSS”. Chromium has a handy tool that shows the parts of the CSS that are used and the parts that are not. My next challenge will be to produce minimized versions of these CSS stylesheets, that contain only the lines that are actually used. Likely I will break the site again, but I will make backups of the original versions. As usual, I will use this blog to write updates that might help other people from making the same mistakes.

Published on: 27 november 2018

a silhouette of a person's head and shoulders, used as a default avatar

The psychology behind why humans suck at reading performance data

People often think that performance testing frameworks exist because machines are good at finding patterns in performance data and humans are not.

Actually, humans are very good at finding patterns in data. In fact, we’re too good. We see patterns in data where none exist because our minds are hardwired to notice them. Detecting patterns and ascribing meaning to them was once thought to be a psychotic thought process linked with schizophernia, but psychologists now understand it to be an evolutionary skill which allows us to make predictions about the world we inhabit.

But those pattern recognition skills that helped our ancestors find food in the wild have all kinds of consequences ranging from the bizarre (sometimes we see faces in clouds) to the downright illogical (thinking a coin that has turned up heads for the last few flips is likely to be heads next time).

And it’s because of this cognitive bias that we are really bad at reading and comparing performance numbers without the help of performance analysis tools – our brains simply cannot view the data without trying to find patterns.

For long-running performance tests, it’s common to run the test case standalone, outside of the test suite, for example when changing the code in between runs. But if you’ve ever eyeballed a test result instead of using the reporting framework of your chosen test suite, you’ve potentially fallen victim to this quirk of human nature.

Measuring performance is hard. Let the machines help.

a silhouette of a person's head and shoulders, used as a default avatar

Qactus 1.0 is out!

Here is the next generation of Qactus – it is now an OBS client and not just an OBS notifier.
The main feature of this release is an OBS browser for exploring and maintaining projects/packages; you can branch or create a project or a package, upload, download or delete files, or check the build log of your favourite package!

Changes in a nutshell:
– New: OBS browser
– New: build log viewer
– New: system proxy support
– Improved request state editor: coloured SR diffs, a tab for SR build results
– Bugs fixed, several small enhancements

And here is the mandatory screenshot 🙂

RPM packages are being built and will be available soon.

the avatar of Nathan Wolf

the avatar of Nathan Wolf

the avatar of Sebastian Kügler

Different indentation styles per filetype

For my hacking, I love to use the KDevelop IDE. Once in a while, I find myself working on a project that has different indentation styles depending on the filetype — in this case, C++ files, Makefiles, etc. use tabs, JavaScript and HTML files use 2 spaces. I haven’t found this to be straight-forward from KDevelop’s configuration dialog (though I just learnt that it does seem to be possible). I did find myself having to fix indentation before committing (annoying!) and even having to fix up the indentation of code committed by myself (embarrassing!). As that’s both stupid and repetitive work, it’s something I wanted to avoid. Here’s how it’s done using EditorConfig files:

  1. put a file called .editorconfig in the project’s root directory
  2. specify a default style and then a specialization for certain filetypes
  3. restart KDevelop

Here’s what my .editorconfig file looks like:


# EditorConfig is awesome: https://EditorConfig.org

# for the top-most EditorConfig file, set...
# root = true

# In general, tabs shown 2 spaces wide
[*]
indent_style = tab
indent_size = 2

# Matches multiple files with brace expansion notation
[*.{js,html}]
indent_style = space
indent_size = 2

This does the job nicely and has the following advantages:

  • It doesn’t affect my other projects, so I don’t have to run around in the configuration to switch when task-switching. (Editorconfigs cascade, so will be looked up up in the filesystem tree for fallback styles.
  • It works across different editors supporting the editorconfig standards, so not just KWrite, Kate, KDevelop, but also for entirely different products.
  • It allows me to spend less time on formalities and more time on actual coding (or diving).

(Thanks to Reddit.)

the avatar of Klaas Freitag

Change in Professional Life

This November was very exciting for me so far, as I was starting a new job at a company called Heidolph. I left SUSE after working there for another two years. My role there that was pretty far away from interesting technical work, which I missed more and more, so I decided to grab the opportunity to join in a new adventure.

Heidolph is a mature German engineering company building premium laboratory equipment. It is based in Schwabach, Germany. For me it is the first time that I am working in company that doesn’t do only software. At Heidolph, software is just one building block besides mechanical and electronic parts and tons of special know how. That is a very different situation and a lot to learn for me, but in a small, co-located team of great engineers, I am able to catch up fast in this interesting area.

We build software for the next generation Heidolph devices based on Linux and C++/Qt. Both technologies are in the center of my interest, over the years it has become more than clear for me that I want to continue with that and deepen my knowledge even more.

Since the meaning of open source has changed a lot since I started to contribute to free software and KDE in particular, it was a noticeable but not difficult step for me to take and move away from a self-proclaimed open source company towards a company that is using open source technologies as one part of their tooling and is interested in learning about the processes we do in open source to build great products. An exciting move for me where I will learn a lot but also benefit from my experience. This of course that does not mean that I will stop to contribute to open source projects.

We are still building up the team and look for a Software Quality Engineer. If you are interested in working with us in an exciting environment, you might wanna get in touch.

the avatar of Nathan Wolf

the avatar of Federico Mena-Quintero

Propagating Errors

Lately, I have been converting the code in librsvg that handles XML from C to Rust. For many technical reasons, the library still uses libxml2, GNOME's historic XML parsing library, but some of the callbacks to handle XML events like start_element, end_element, characters, are now implemented in Rust. This has meant that I'm running into all the cases where the original C code in librsvg failed to handle errors properly; Rust really makes it obvious when that happens.

In this post I want to talk a bit about propagating errors. You call a function, it returns an error, and then what?

What can fail?

It turns out that this question is highly context-dependent. Let's say a program is starting up and tries to read a configuration file. What could go wrong?

  • The file doesn't exist. Maybe it is the very first time the program is run, and so there isn't a configuration file at all? Can the program provide a default configuration in this case? Or does it absolutely need a pre-written configuration file to be somewhere?

  • The file can't be parsed. Should the program warn the user and exit, or should it revert to a default configuration (should it overwrite the file with valid, default values)? Can the program warn the user, or is it a user-less program that at best can just shout into the void of a server-side log file?

  • The file can be parsed, but the values are invalid. Same questions as the case above.

  • Etcetera.

At each stage, the code will probably see very low-level errors ("file not found", "I/O error", "parsing failed", "value is out of range"). What the code decides to do, or what it is able to do at any particular stage, depends both on the semantics you want from the program, and from the code structure itself.

Structuring the problem

This is an easy, but very coarse way of handling things:

gboolean
read_configuration (const char *config_file_name)
{
    /* open the file */

    /* parse it */

    /* set global variables to the configuration values */

    /* return true if success, or false if failure */
}

What is bad about this? Let's see:

  • The calling code just gets a success/failure condition. In the case of failure, it doesn't get to know why things failed.

  • If the function sets global variables with configuration values as they get read... and something goes wrong and the function returns an error... the caller ends up possibly in an inconsistent state, with a set of configuration variables that are only halfway-set.

  • If the function finds parse errors, well, do you really want to call UI code from inside it? The caller might be a better place to make that decision.

A slightly better structure

Let's add an enumeration to indicate the possible errors, and a structure of configuration values.

enum ConfigError {
    ConfigFileDoesntExist,
    ParseError, // config file has bad syntax or something
    ValueError, // config file has an invalid value
}

struct ConfigValues {
    // a bunch of fields here with the program's configuration
}

fn read_configuration(filename: &Path) -> Result<ConfigValues, ConfigError> {
    // open the file, or return Err(ConfigError::ConfigFileDoesntExist)

    // parse the file; or return Err(ConfigError::ParseError)

    // validate the values, or return Err(ConfigError::ValueError)

    // if everything succeeds, return Ok(ConfigValues)
}

This is better, in that the caller decides what to do with the validated ConfigValues: maybe it can just copy them to the program's global variables for configuration.

However, this scheme doesn't give the caller all the information it would like to present a really good error message. For example, the caller will get to know if there is a parse error, but it doesn't know specifically what failed during parsing. Similarly, it will just get to know if there was an invalid value, but not which one.

Ah, so the problem is fractal

We could have new structs to represent the little errors, and then make them part of the original error enum:

struct ParseError {
    line: usize,
    column: usize,
    error_reason: String,
}

struct ValueError {
    config_key: String,
    error_reason: String,
}

enum ConfigError {
    ConfigFileDoesntExist,
    ParseError(ParseError), // we put those structs in here
    ValueError(ValueError),
}

Is that enough? It depends.

The ParseError and ValueError structs have individual error_reason fields, which are strings. Presumably, one could have a ParseError with error_reason = "unexpected token", or a ValueError with error_reason = "cannot be a negative number".

One problem with this is that if the low-level errors come with error messages in English, then the caller has to know how to localize them to the user's language. Also, if they don't have a machine-readable error code, then the calling code may not have enough information to decide what do do with the error.

Let's say we had a ParseErrorKind enum with variants like UnexpectedToken, EndOfFile, etc. This is fine; it lets the calling code know the reason for the error. Also, there can be a gimme_localized_error_message() method for that particular type of error.

enum ParseErrorKind {
    UnexpectedToken,
    EndOfFile,
    MissingComma,
    // ... etc.
}

struct ParseError {
    line: usize,
    column: usize,
    kind: ParseErrorKind,
}

How can we expand this? Maybe the ParseErrorKind::UnexpectedToken variant wants to contain data that indicates which token it got that was wrong, so it would be UnexpectedToken(String) or something similar.

But is that useful to the calling code? For our example program, which is reading a configuration file... it probably only needs to know if it could parse the file, but maybe it doesn't really need any additional details on the reason for the parse error, other than having something useful to present to the user. Whether it is appropriate to burden the user with the actual details... does the app expect to make it the user's job to fix broken configuration files? Yes for a web server, where the user is a sysadmin; probably not for a random end-user graphical app, where people shouldn't need to write configuration files by hand in the first place (should those have a "Details" section in the error message window? I don't know!).

Maybe the low-level parsing/validation code can emit those detailed errors. But how can we propagate them to something more useful to the upper layers of the code?

Translation and propagation

Maybe our original read_configuration() function can translate the low-level errors into high-level ones:

fn read_configuration(filename: &Path) -> Result<ConfigValues, ConfigError> {
    // open file

    if cannot_open_file {
        return Err(ConfigError::ConfigFileDoesntExist);
    }

    let contents = read_the_file().map_err(|e| ... oops, maybe we need an IoError case, too)?;

    // parse file

    let parsed = parse(contents).map_err(|e| ... translate to a higher-level error)?

    // validate

    let validated = validate(parsed).map_err(|e| ... translate to a higher-level error)?;

    // yay!
    Ok(ConfigValues::from(validated))
}

Etcetera. It is up to each part of the code to decide what do do with lower-level errors. Can it recover from them? Should it fail the whole operation and return a higher-level error? Should it warn the user right there?

Language facilities

C makes it really easy to ignore errors, and pretty hard to present detailed errors like the above. One could mimic what Rust is actually doing with a collection of union and struct and enum, but this gets very awkward very fast.

Rust provides these facilities at the language level, and the idioms around Result and error handling are very nice to use. There are even crates like failure that go a long way towards automating error translation, propagation, and conversion to strings for presenting to users.

Infinite details

I've been recommending The Error Model to anyone who comes into a discussion of error handling in programming languages. It's a long, detailed, but very enlightening read on recoverable vs. unrecoverable errors, simple error codes vs. exceptions vs. monadic results, the performance/reliability/ease of use of each model... Definitely worth a read.

the avatar of Nathan Wolf