minimalistic IP country lookup
]]>I strive to optimise this blog's performance as well as I can. But chasing a goal of a lightweight website while keeping it pretty prevented me from realising the obvious truth that the most performant assets are… no assets.
So, inspired by Sijmen J. Mulder's directory of text-only websites, I decided to create a bare version of my blog.
Here's how it went:
Many pages of this blog are already available in multiple formats, eg. the Atom feed of all entries or a JSON version of the list of my projects, so adding a new one, .lite
, was relatively easy.
The main part was copy-pasting all <page-type>.html.twig
files to <page-type>.lite.twig
and removing all the bullshit from them. All the HTML nodes that exist solely to make the page look nicer: wrappers, containers, columns, etc. All the icons, fonts, logos, twemoji, everything that's not essential.
There's no JavaScript loaded whatsoever.
There's no external stylesheets loaded, just some minimal styling in <style>
tag and few inline style
attributes.
Images inside articles had to stay, becuase in many cases they are a very important part of the content, and not just decoration. But I made them way smaller – max 240px in width – and linked to open a bigger version, if necessary. I also replaced the JS-based lazy loading with HTML5 native loading="lazy"
.
However, the browser support for this feature is still not perfect – and if it doesn't work on someone's machine, they'll have to download 8 MB (!) of tiny images when visiting /blog.lite
… So I implemented a simple pagination, all inside a Twig template, based on ?after=<timestamp>
parameter in the query string.
Anyways… It's time for the results! 🥁
Normally, opening the the homepage makes 40 requests and loads 522 kB of resources. In the lite version, it's 2 requests (HTML and favicon) that weight just 15.8 kB. Just 3% of the original weight!
The heaviest page, /blog
makes 37 requests of 3.5 MB. In the lite version, it's 10 requests of 198 kB. Just 6% of the original weight.
Opening a random article made 34 requests of 493 kB normally, and just 3 requests of 31.2 kB in the lite version. Also just 6% of the original weight.
The differences are huuuuuuuge!
Summing up: if you don't mind websites looking ascetic in return for loading quicker, working smoother, and sparing data plan and battery, definitely check out Sijmen's directory.
And on my blog you can just head to /lite or add .lite
at the end of any subpage. 😉
Hackers know your password. I'm like 99% sure they do. Just go to ';--have i been pwned? and enter your email(s). See? Your password is as good as public.
We all hate passwords, don't we? Trying to keep them easy to remember, but also hard to break, while also complying with stupid arbitrary rules, while having corporate forcing you to change it regularly... They're a pain in the ass.
They aren't even that safe. How can you be sure that the administrator of a website that you trusted with your password even hashes it? How do you know they salt it? How do you know they don't use an outdated hashing algorithm? How can you be sure they won't have a data breach, their database leaked, and your password recovered with supercomputers?
Damn, I'm an administrator myself, and I still can't be sure I do everything right. I got a panic attack last week when a user reported that his password was stolen and used for blackmail ( Recognising red flags in blackmail emails) – in this case it was a reused password that leaked somewhere else, but still... Even though I'm storing people's passwords in the best way I can, I would feel so much better, if I just didn't have to store them.
How about we just stop using passwords?
If you log in with Facebook, Twitter, Google, Apple or whatever, you make me a happier developer. Yes, your social media account is still protected by a password, but at least I don't know it. If anything happens to your social media account, it's gonna be a problem of a huge corporation with well-funded infosec department – not a random developer who makes websites for fun and does their best keeping them safe.
In my first job I saw in the logs that many users use the “remind password” feature not as a recovery option, but as their main login method. Today, their quirk is becoming an increasingly popular security trend, actually. If you go, for example, to Whereby, they'll only ask you for your email, not a password. Every time you try to log in, they'll just send you and email with a one-time code.
I implemented a similar approach in my new project, Avris Booster: Quick start of new projects (under active development). Now I don't need a separate “register”, “log in” and “remind password” forms – I just have one. Now I don't keep any passwords – just some temporary codes that can't be reused on other websites.
And users don't have to worry about remembering, storing or losing their passwords.
Whatever authentication method you use (or are forced to use), it's always better to have a second one. If a website offers MFA, it's smart set it up (I can strongly recommend the Authy app for it).
This way, even if your password gets stolen, the hackers will still need more (your phone) to access your account.
There's also such a thing as Hardware Security Modules. Some websites offer login with PGP keys. And there's probably a lot more options, but no time to dive into them right now.
Let's face it, passwords are still inevitable. Until webmasters stop being so password-centric, we have to keep using passwords, if we want to keep using their products.
The best we can do in this situation, is to make sure that all of our passwords are strong and unique (so that a hacker who found out our Spotify password can't use it to log in to our bank or email). Of course, nobody can remember tens or hundreds of strong, unique password. That's why we have password managers. I can strongly recommend KeePass. Or even saving the passwords in the browser.
Seriously, anything is better than using the same password for some shady forum as you use for your online banking.
]]>I've seen some begginer programmers asking themselves: why do I even need constants? Variables I get, they're super important, but why have an extra thing that's like a variable, but worse? It can't even change! And if I know that const NUMBER_OF_COLUMNS = 3
, why can't I just write 3
?
Well, first of all, it's not always just a simple 3
. Sometimes, for example, a constant is 3.141592653589793...
. In this case writing Math::PI
is not only shorter, prettier, and more meaningful, but usually more accurate (how many digits of π can you write down? your standard library knows a lot of them) and not prone to typos (misspelled constant name just won't compile...).
let circ1 = 2 * Math.PI * radius;
let circ2 = 2 * 3.141582653589793 * radius;
The first line better resembles the well-known mathematical formula, and it doesn't require googling (or remembering) the actual value. The second one? Apart from being ugly, it's also wrong. Look closely.
Creating a constant with some value gives this value a label. Gives it an extra meaning. It's not just a three anymore. Now it's the NUMBER_OF_COLUMNS
(that just happens to be three).
Now it's a three that's different from other threes.
Let's say you have 3
columns of data updated every 3
minutes. Your code is full of threes. After coming back to your code after a break, you don't know anymore what those threes mean, they're just some numbers. If one day you have to change the layout to two columns, how do you know which threes to replace with twos, and which ones to keep?
Do you have to look through your entire codebase, read it and understand it?
Or maybe instead you have NUMBER_OF_COLUMNS
columns of date updated every UPDATE_INTERVAL
minutes?
const NUMBER_OF_COLUMNS = 3;
const UPDATE_INTERVAL = 3;
Easy to understand wherever they are used, and super easy to change in just a single place.
Constants are only constant during the execution. During development – the actually make changes easier.
And yes, you can do “less” with them, but that property actually adds value. It lets the programmers (and the compiler) know that it's not supposed to change. It prevents your app from being inconsistent because someone accidentally overwrote a variable.
So there you go: constants, although less flexible, are just as useful as variables.
]]>It wasn’t really supposed for the New Year, but I’ve had plenty of free time on my hands during the holiday break, so here it is already: a brand new version of my blog 🥳
Where to start? I got increasingly annoyed by the old version. The design started seeming boring and blunt. The code started growing unstable. It was using the Micrus framework, which I stopped supporting a year ago. It was using Micrus Assetic – an outdated method of asset management, which highly depended on the server configuration and binaries installed. The deployments were “iffy”, to say the least. I was afraid of sending even small changes to the server, for fear something might blow up again. Some small things I didn’t even notice were wrong: like still warning about Google Analytics tracking, even though I migrated to Matomo ages ago.
I decided to keep it simple. To go with a “less is more” approach. I’ve removed soooo many features that just weren’t useful enough.
I used to have an admin panel: except I didn’t need any fancy editor, the main content was written in an (enhanced) Markdown anyway. So for this version, I’ve decided for a filesystem-based approach: I’ve ditched the database completely, I don’t need any ORM, I don’t need an admin panel. I just have a bunch of SUML files tracked by GIT. Using Esse CMS.
I used to have three language versions of the UI – but why? I don’t think anyone really cared about not seeing the names of the categories in Polish, when most of recent my posts were in English anyway. So now the posts are still available in the language(s) they were written in, but I’m no longer maintaining the multiple versions of the UI, or the features to filter content by language. What a relief!
I used to have a “random post” box. I used to have tag clouds. I used to have an embedded Twitter timeline. I used to have the latest posts on the “about me” page (really?). I used to have a link shortening feature (”avris.it/l/<whatever>”) that I’ve never used, not a single time. I used to have a separate controller and database fields to support redirects for legacy URLs from the even earlier version. I used to have database fixtures. I used to have a console command to export legacy comments to Disqus. I used to have tooltips and popovers. All gone now! 😍
I replaced the infinite scroll with... just loading the whole page at once 🤷 It’s not that big for today’s standards anyway – the heaviest list of posts is 113 KB of compressed HTML. Instead, I now lazy load images using the IntersectionObserver
API. Way simpler than infinite scroll or pagination – which aren’t really necessary with this amount of data.
Oh, and the website is now using a fast an sleek Symfony setup with an HTTP cache 🥰
Design-wise, I use the default Bootstrap theme, with only minor changes of variables and minimal additional styling. The main feature, obviously, are the tilted elements. That’s enough to add some personality to the website, without making it overcomplicated. Compared to the previous version, it’s way more contrasting, better tailored for wider screens and clearer.
Overall, 41090 (!) lines of code were removed:
There’s nothing else in the (programming) world I love more than removing useless code!
Deployments are now stable, simple, reproducible and revertible, thanks to Symfony, Webpack, lack of database (no db = no migrations), and most importantly my recent child: Avris Deployer.
So, to finish this post, let me just quickly show you the four versions I’ve been through so far:
My first blog was called “Silva Idearum” (Latin for “Forest of ideas”). It was anonymous, spirituality-oriented, Polish-speaking, Joomla-running and with a “too much” design.
After coming out and becoming independent from my parents, I was finally able to put my name on my posts. New domain and a redesign was also in place.
As announced in Brand new blog…
YAML gets praised for being clear and human readable, but it’s also criticised for being ambiguous.
SUML is an attempt to keep the good things about YAML but remove its ambiguity and needless complexity.
]]>The PHP ecosystem is full of frameworks: Symfony, Laravel, Yii, Zend, Phalcon, and so many, many, many more... All of them built by professionals and supported by big communities. So why on earth would a junior developer, who has just started his first job, try his hand in building yet another one?
Well, here’s why:
When I learned PHP, the URLs I knew how to build and use looked like this: /index.php?module=user&action=show&id=123
. I had no idea, how to make them nice like this: /user/show/123
. Symfony knew though, and I could just use it.
But I was used to coding things from scratch and understanding how they work internally, I didn’t like the magic of “it just works”. I know, stupid, if I were still doing that, I’d never finish any project. At the time it did make sense, though.
I was curious if I can replicate the behaviour. I stole the .htaccess
config from Symfony, I’ve expanded my knowledge about regular expressions, and eventually, I did it! It was buggy and ugly, but it worked and it was mine, and I was so proud of it!
And then I couldn’t stop. Every time I had had to dig into Symfony’s code and had gotten overwhelmed by its complexity, I realised how simple the overall logic actually is, even though it’s overblown by trying to keep it generic and open for modification/extension. So just like I did with the router, I also rewrote from scratch the event dispatcher, the security layer, the DI container and a couple of other components, eventually ending up with a fully usable framework.
I don’t think I have access to its source code anymore, but I think it was only 10 files or so. It was crappy, didn’t comply with PSR-4, wasn’t published as a Composer package, and probably was full of bugs that I’ve never found.
It didn’t really make much sense to further develop it. Nobody’s gonna use it anyway. It’s not gonna be better than any of the frameworks that have plenty of people working on them. I’ve already learned a lot, isn’t it enough to call it a day?
But I took some criticism for the version 0.0 from people who knew better how to code, so I had to address their advice and to make Micrus better. And it turned out to be a great decision because I still had a lot more to learn.
The biggest challenge was managing dependencies. I couldn’t put all the modules like “Mailer”, “Twig”, “Social login”, “CRUD” etc. in one huge package and still call it “Micrus”. But I was developing all of them at once! I had to learn more about the internals of Composer in order to treat local directories as if they were packages, without pushing or tagging them (only later I found out about Repository of type “path” – really useful!). I had to learn how to organise the code better so that adding or removing entire modules could be as seamless as possible. I had to keep them consistent, simple, powerful, extendable. I spent hours and hours, and days and days, on figuring out their architecture...
The second biggest challenge was the forms. They’re just awful. The form abstraction layer has to move data around between the actual object that holds data, a form and it’s HTML representation. It needs to map the data both ways, it needs to validate it, it’s just a mess. At work at Rocket Internet, even though we were using a Phalcon-based framework, many ventures decided to use the standalone Symfony Form component instead of Phalcon forms, because that was the least troublesome one.
Still, they weren’t simple enough for me. Sure, they’re way, way better than handling the forms without any framework, but whenever I needed to do anything even slightly non-standard, I really missed the option of just writing down some HTML. Instead, I had to learn about the internals of the framework. I didn’t like it, so, I’ve spent ages trying to prove to myself, that one can make the best of two worlds.
The same with the security layer. I really hate how Symfony implements it. It’s a piece of cake to configure the most simple cases, they hide everything behind a veil of magic. But as soon as you need something non-standard, you need to go through their documentation, get into their way of thinking, understand their ideas... You can’t just write down the simple logic you need, you need wrappers, voters, providers, listeners, etc. etc. I decided to try to implement it my way.
And it worked for me. I’ve released Micrus • Tiny, yet powerful, PHP framework that later become Micrus v4 • Beauty of simplicity. I’ve built a couple of projects based on Micrus (listed at micrus.avris.it). I had fun, seeing how easy it was for me to build apps with Micrus.
Whenever I would encounter something worth improving, I did so. How exhausting was that! On one hand disappointing (I wasn’t expecting anyone else to use it), on the other really rewarding (it is an accomplishment after all). When I discovered The non-magic of autowiring, I implemented that as well. I covered 100% of my code with unit tests (which was quite a challenging type of code to cover).
But then, Symfony has released its version four. And it’s amazing. It has Flex, it’s flexible, it’s fast... Maybe it’s not as simple as I would wish it to be, but it’s undeniably way better than anything I could ever create myself. Also, I simply got tired of building Micrus already. It grew bigger than I thought it would, it started resembling Symfony more and more, while I understood more and more of Symfony’s design...
So there I am: having learned an awful lot about standards, modularisation, architecture, framework internals, autoloading, autowiring, testing, managing dependencies, and much much more. Having created something irrelevant, but quite impressive nevertheless.
I’m still gonna keep putting Micrus in my CV, I’m still gonna keep most of my Micrus-based-projects running it, but I’m not gonna improve it anymore or start any new projects on it.
It’s been a wonderful adventure. Now it’s time to move on 😊
]]>It’s honestly diffucult being a webdeveloper in the world of shitty websites. I guess that’s how hairdressers feel when they see my pathetic hair after it’s been a while since my last visit...
But the thing is, even though it’s technically easy to use scissors and clippers, I don’t do that on my own hair, I leave that to the professionals.
It’s great that HTML is so easy to learn and that many schools teach it to children. But its simplicity is also a curse, leaving some people convinced that “they can now do programming” (HTML is a markup language, not a programming language) and that the whole thing is easy.
And, admittedly, writing code that does stuff and solves problems isn’t that hard either, especially if you have Google and StackOverflow on your side. The hard part is to write this code is such a way, that another person can understand it, maintain it and modify it, so that groups of programmers can colaborate on it together, so that it can easily be kept up to date... I’ve learned how to write code when I was twelve. But learning, how to write good, maintainable code, took me years of professional work, and I still have a lot to learn.
It’s also difficult for the professionals to keep up to date with technology, it changes so rapidly. For example, a teacher of webdevelopment at a university (!) was teaching my class how to use HTML4 and how to deal with the ISO-8859-1 encoding, even though both HTML5 and UTF-8 have been standards for years!
The same goes, I guess, for the author of wnbr.nl:
It’s not a bad website. It gives you the information you need, it doesn’t hurt your eyes, it even uses HTML5 and UTF-8 (so much better than my uni...). On the other hand there’s plenty one could do to make it better:
And while it’s completely fine if a personal website or a side project looks like that, I feel really sad when I see a website of a doctor, a business, or a big event, that is so far away in the past... Plenty of people are working on constant improvements of the standards, on new technologies, new approaches, new APIs – and yet, as a user, I’m still left with not being able to make a doctor’s appointment online, as if it was so damn hard to implement... Spending your whole life on digitalising the world bit by bit, but not being able to fully enojoy that digitalisation as a user really sucks...
Anyways... As a person, who wants to make the world (also the cyber-world) a better place, who really enjoyed the WNBR this year and who happened to have some free time on my hands, I contacted the organisers and offered them my help in bringing their website to the 21st century.
I assumed they wouldn’t want it, actually. Somebody created the current website ( nsesoftware.nl), somebody is proud of it, and obviously nobody likes criticism... So I tried to be nice, not to criticise, but to offer some ideas and help.
I got no answer though, and I really needed to start right away, as long as I had free time and the motivation to work on it. I ended up with something like this:
I got nothing but positive feedback from all the friends whom I’ve shown the end result. Still, I got no response from the WNBR people on wheather or not they actually want it. But since it’s almost done already, I’ve sent them a demo, saying that if they do, it’s free for them to use.
After a while I kind of got an answer – not a “no, thanks”, or even “fuck off”... instead, I just got blocked on Twitter. So childish... Well, that’s how you lose like a 1000€ worth of free service, too bad for them.
Anyways... a couple of days ago I stumbled upon the website polyamorynetwork.com. I totally fell in love with their logo 😍 And even though I’m not a polyamorist myself (yet?), I decided to check it out anyway.
I couldn’t, though. Setting aside that the SSL certificate was expired since a month, which makes browsers give the user a scary red security warning... The registration form still uses reCAPTCHA v1, which got shutdown in March 2018 (and deprecated way earlier). That means nobody was able to join their network for the last half a year, and apparently nobody noticed!
Is the website abandoned? Why can’t it just say so? Or maybe there’s a big, active community there, but it’s closed for new members, because the admin doesn’t give a damn anymore? I guess I’ll never know.
On one hand it’s great that anyone can build their own website – that’s what makes the web thrive, that’s what makes it open and equal!
On the other hand though – it requires way more skill and knowledge to do it right. If you want to look professional, keep that in mind.
So what’s my point? I guess I don’t have any, just wanted to complain a bit.
Except maybe for one thing:
If you have (or want to start) a project, event or a non-profit organisation that I could stand behind (LGBTQ rights, human rights, naturism, non-monogamy, education...) that I could support from the IT side, feel free to contact me 😉
]]>I had to learn Git as a programmer. If you want to easily collaborate on a codebase, you really need either Git or something similar. But as a non-programmer, you’ve probably never even heard that name, have you? Then why would you ever need it?
Well, for exactly the same reasons!
It’s a version control system, which means it remembers how the content of a directory (called “repository”) changed over time. You can decide, wich moments in time are saved – those snapshots of the repository are called “commits”. You can send those commits to a server (it’s called “pushing”), so that other people can download them (”pull”) and collaborate and your project (plus the server works as a backup of your data). If you ever want to revert your repository to how it looked like some time ago, or to see how it changed over time, or who wrote a particular line in a file, etc. etc., Git offers you all of that.
Did you notice, how I didn’t use the word “code” anywhere in the above definition? That’s because Git isn’t about code or programming. Yes, it was created to help with development of the Linux Kernel, but it really doesn’t care what kind of files it handles.
Are you a graphic designer, tired of keeping all those files like project.psd
, final.psd
, final2.psd
, final2-FINAL.psd
, because the client might change their opinion one more time and basically ask you to redo your first project once again? Then Git will help you!
Are you a writer, wanting to make a major change in your book, but afraid it will not work out fine, so you’d like to be able to quickly revert that change? Yup, Git is for you!
Are you a person, who wants to track how their CV changed over time, without keeping all the versions as separate files? Just use Git!
Do you want to collaborate on something with your friends, without sending email attachments back and forth or worrying if everyone has the newest version? Git might be exactly what you need!
Well, not here, this is not a tutorial. The Internet already has plenty of those. Pick some and spend some time with it – you won’t regret it!
]]>A Coding Dojo is a great way to practise programming, test-driven development, teamwork, pair programming and problem solving. Avris Dojo provides an easy way to synchronise your dojo codebase with your teammates.
]]>jQuery used to be virtually indispensable, if you wanted to develop a cross-browser website without getting a headache.
Today, however, you might not need jQuery, especially, if you’re developing a library and want to avoid unnecessary dependencies.
Still, some helpers could be useful... Vanillin is an opinionated set of helpers that I find most useful, a bare minimum to make life easier.
]]>While working on Avris Forms v4.0, I’ve decided to migrate some code from CoffeScript with jQuery to Vanilla JS. And I guess it might be a good idea to share this transition 😉
The code handles the form widget visible in the screenshot above. More specifically: those buttons in the right column. Clicking on the green one adds a new element/row based on a template hidden inside a <script>
tag. Clicking on a red button results in removal of the current row (after asking for a confirmation).
$('body').on 'click', '.form-multiple-add', ->
$form = $(this).parents('.form-multiple')
newIndices = $form.find('[data-index^=new]').map((i, el) -> el.dataset['index'].substr(3)).get()
newIndex = if newIndices.length then Math.max.apply(null, newIndices) + 1 else 0
$template = $($form.find('.form-multiple-add-template').html().replace(/%i%/g, 'new' + newIndex))
$(this).parents('tr').before $template
$template.find(':input:enabled:visible:first').focus()
$('body').on 'click', '.form-multiple-remove', ->
return false if !confirm('Are you sure?'))
$(this).parents('tr').remove()
It was not a part of the library (as the library was supposed to only provide PHP), instead each project included this part on its own. Which led to code duplication (and the worst kind: between projects) and also kinda prevented me from abandoning CoffeeScript in newer projects, even if I wanted to.
And I did want to, because JavaScript has matured as a language over the last years. An additional level of complexity in my projects simply wasn’t necessary anymore. CoffeeScript did a great job of making fontend scripting bearable, but now it’s slowly time for it to get a deserved retirement.
So I’ve decided to integrate the frontend part into the library. Except I shouldn’t be forcing users to use CoffeeScript or jQuery, it should just work out of the box in any project.
Since CoffeeScript compiles into JS (although usually with from some unnecessary returns and similar features), getting rid of it is really simple. After using js2coffee or decaffeinate we would end up with something like this:
$('body').on('click', '.form-multiple-add', function() {
var $form, $template, newIndex, newIndices;
$form = $(this).parents('.form-multiple');
newIndices = $form.find('[data-index^=new]').map(function(i, el) {
return el.dataset['index'].substr(3);
}).get();
newIndex = newIndices.length ? Math.max.apply(null, newIndices) + 1 : 0;
$template = $($form.find('.form-multiple-add-template').html().replace(/%i%/g, 'new' + newIndex));
$(this).parents('tr').before($template);
return $template.find(':input:enabled:visible:first').focus();
});
$('body').on('click', '.form-multiple-remove', function() {
if (!confirm('Are you sure?')) {
return false;
}
return $(this).parents('tr').remove();
});
Removing the dependency on jQuery is more tricky.
There are some nice lists that show you how to replace some jQuery usage with plain JS. Those Javascript versions used to be longer, more complicated and require some browser-specific checks and adjustments. Those times are almost over though (as you can see from the list), so the need for jQuery gets smaller and smaller.
Replacing those calls from the list with Vanilla JS’s counterparts was not enough in my case. I needed two functionalities more: finding an element’s parent by a selector ($el.parents('.foo')
) and binding an event listener to elements matching a selector, even if those elements don’t exist yet at the moment of binding ($('body').on('click', '.foo', => ... )
).
They were surprisingly easy to implement:
var findParent = function(el, selector) {
do {
if (el.matches(selector)) {
return el;
}
el = el.parentNode;
} while (el && el.matches);
};
var on = function (selector, event, handler) {
document.addEventListener(event, function (e) {
var match = findParent(e.target, selector);
if (match) {
var result = handler.apply(match, [e]);
if (result === false) {
e.preventDefault();
e.stopPropagation();
}
}
});
};
With those two helpers I could transform my code to this vanilla JavaScript:
on('.form-multiple-add', 'click', function () {
var form = findParent(this, '.form-multiple');
if (!form) {
return;
}
var newIndices = Array.from(form.querySelectorAll('[data-index^=new]')).map(function (el) {
return el.dataset['index'].substr(3);
});
var newIndex = newIndices.length ? Math.max.apply(null, newIndices) + 1 : 0;
var template = form.querySelector('.form-multiple-add-template').innerHTML.replace(/%i%/g, 'new' + newIndex);
findParent(this, 'tr').insertAdjacentHTML('beforebegin', template);
var firstInput = form.querySelector('[data-index=new'+newIndex+'] :enabled');
if (firstInput) {
firstInput.focus();
}
});
on('.form-multiple-remove', 'click', function () {
if (!confirm('Are you sure?')) {
return false;
}
var row = findParent(this, 'tr');
row.parentNode.removeChild(row);
});
Not bad, is it? 😉
]]>Keeping your classes immutable and stateless makes your code way less prone to bugs. Yet somehow this clean code rule isn’t as popular and as often invoked as SRP, YAGNI, DRY, KISS and others... Maybe it’s because of the lack of a catchy acronym?
Anyways, I’d like to take a look at two examples of when sticking to this rule could save your ass (or at least save you some time debugging).
PHP offers a DateTime
object that encapsulates, well, a date and time. And it’s pretty cool: it can parse strings like “next monday”, it can understand instructions like modify “+1 week midnight”, the standard library offers support for time zones and for iterating over time (like from “now” to “+1 day” every “+30 minutes”).
But there’s a huge catch involved: DateTime
is mutable.
Let’s say you have a User
entity:
/**
* @ORM\Entity
* @ORM\Table
**/
class User
{
/**
* @var \DateTime
* @ORM\Column(type="datetime")
*/
private $createdAt;
// many other fields...
public function __construct()
{
$this->createdAt = new \DateTime();
}
public function getCreatedAt(): \DateTime
{
return $this->createdAt;
}
}
It looks as if you could be sure that createdAt
will be stored in the database as exactly the moment when the entity was first created, right? After all there’s no setter given, so unless you hack around with reflections, there’s no way to modify that value ever again, is there?
Well, let’s say in your app it’s important to display, how many Sundays have passed since some stuff happened, and one of them is the registration of a given user.
public function countSundays(\DateTime $datetime): int
{
$datetime->modify('next sunday 23:59:59');
$sundays = 0;
while ($datetime < new \DateTime('now')) {
$sundays++;
$datetime->modify('+1 week');
}
return $sundays;
}
At first glance this code seems fine as well. We get some value, we use it to calculate some other value, and we return the result. But notice that we keep modifying the input data! If you did it with a string, int or an array, the input would not be modified at all (unless explicitly passed as reference, &$string
). But \DateTime
, although it’s just a simple value wrapped in a class, is a class after all, so it’s always passed by reference.
So even though you cannot set User
’s createdAt
to a different object, you can pass that object to some function that will modify the inside of that object. So if you happen to save your User
entity to the database after its number of Sundays has been calculated, you will end up with all the users having been registered in the future and constantly changing their registration date.
It might be quite difficult to find the source of this bug. After all, there is no setter, and the connection between this date and countSundays
might not be too obvious – maybe hidden in a Twig extension or an external library...
How to mitigate that? Ideally, use DateTimeImmuttable
, which “behaves the same as DateTime except it never modifies itself but returns a new object instead”.
Or if you don’t want to adjust your whole project to use DateTimeImmuttable
, you might instead remember to clone
a DateTime
variable before passing it anywhere.
Btw, in the Rust programming language all variables are immutable by default 😍
I had to work on a code like this recently:
invoiceService = new InvoiceService
shop = new Shop
invoices = shop.loadInvoices
for each invoice in invoices {
invoiceService.saveInvoice(invoice)
}
And somewhere inside saveInvoice
, this function was executed:
function runSQL(sql) {
result = database_exec(sql)
if result is false {
log(database_error)
die
}
}
The code was PHP, but it’s such a govnokod that the above pseudocode with ~90% of original logic removed, is the best way to show it without crying...
So the problem was: no invoices are generated anymore. Can you see why? If any invoice fails, the script just logs the error and just dies. When it runs again, it tries with the same invoice and dies again, over and over again, never even trying to handle any other invoice.
The underlying error was easy to find and reasonably easy to fix. But we wanted to also make that old, shitty code just a tiny bit better – if one invoice fails, it shouldn’t block all the others anymore.
Instead of die
-ing, the runSQL
would throw an exception. All the other places, where runSQL
is used, would basically work as they used to (uncaught exception would terminate the script anyway), but on loading the invoices we would just catch the exception and carry on with all the other invoices.
What could possibly go wrong?
Well, if you do it on Friday, while starting to get a bit sick, and with InvoiceService
class consisting of >1000 lines of messy, overcomplicated code – a lot can go wrong.
All because InvoiceService
is stateful. That means running invoiceService.saveInvoice(invoice)
might result in different stuff actually being done, depending on the context it was run in. In this case, the class was keeping invoice items (and other stuff) as a private object property – first loading them from somewhere, then saving to the database, and finally cleaning it up.
Obviously, with the try-catch, the last step wouldn’t be executed. So if invoiceService.saveInvoice(invoice1)
fails, then invoiceService.saveInvoice(invoice2)
wouldn’t really save the invoice2
, as name suggests – it would load invoice2
plus some failing items from invoice1
... The errors pile up, the invoice items accumulate, and we end up with hundreds of invalid (empty) invoices with the last one claiming that someone owes us 1,9 mln €...
Obviously not. Some types of classes you expect to be stateful. What would be the point of a Builder, if it couldn’t hold an internal state? How to implement Events elegantly, if they were immutable?
Form libraries like Symfony Forms or Avris Forms require you to provide getters and setters for your entity – and it reflects how your database probably works: a user might change their username or avatar, so the programmer shouldn’t expect them to be immutable. (Unless you’re using Event Sourcing, then good for you!).
Basically, it’s all about what you would expect from a class, see Principle of least astonishment.
A simple data value objects, like date and time, or monetary value should consistently represent the same value. Services that offer a method handleFoo(Foo)
should behave exactly the same for the same Foo
, regardless of when were they triggered.
Entities though can be mutable in your project, and that’s ok. Builders, by their nature, are mutable, and that’s fine. You can even see that from the methods they provide: addFoo
etc. suggest you shouldn’t expect immutability from them.
I think that Rust’s approach should be followed: immutability by default, mutability explicitly declared. In other terms: write immutable classes, unless you can find a good justification for making one mutable.
And when it comes to finding a nice acronym for this rule... how about JAM?
Justify All Mutants.
]]>A dependency injection container with autowiring
composer require avris/container
Container resolves dependencies between defined services, in order to simplify the development process, avoid duplication of code, facilitate interoperability and improve maintainability and testability. See: Dependency Injection pattern.
$parameterProvider = new SimpleParameterProvider(['ROOT_DIR' => __DIR__]);
// parameter provider is optional
$container = new Container($parameterProvider);
$container->set('number', 4);
$container->set(Foo::class, new Foo);
$container->setDefinition(Bar::class, [
'arguments' => [
'$foo' => '@' . Foo::class,
'$dir' => '%ROOT_DIR%/bar',
'$number' => '@number',
'$float' => 69.123,
],
'public' => true,
]);
$container->setDefinition(BarInterface::class, Bar::class); // alias
$container->get('number'); // 4
$container->get(Foo::class); // new Foo
$container->get(BarInterface::class); // new Bar(new Foo, __DIR__ . '/bar', 4, 69.123)
$container->getParameter('ROOT_DIR'); // __DIR__
class
– the class of the service, will default to the service name if not given. If the given class implements the Resolver
interface, it will be instantiated, and it’s resolve
method executed to provide an actual value to be put in the container.
arguments
– constructor arguments.
calls
– method calls to be executed right after constructing the service (setter injection etc.)
'calls' => [
['setLogger', ['@logger']],
['registerListener', ['@listenerA']],
['registerListener', ['@listenerB']],
],
tags
– an array of string that help group similar services together; tagged services can be injected with #tagName
:
$container->setDefinition(HandlerA::class, ['tags' => 'handler']);
$container->setDefinition(HandlerB::class, ['tags' => 'handler']);
$container->setDefinition(HandlerC::class, ['tags' => 'handler']);
$container->setDefinition(Manager::class, ['arguments' => ['$handlers' => '#handler']]);
factory
-- determines if each get
should create a new service (true
), or should one service be reused (false
, default).
resolve
– instead of using class
+ arguments
to construct the service, you can use resolve
to define how it should be created:
$container->setDefinition('foo', ['resolve' => 4]); // 4
$container->setDefinition('language', ['resolve' => '@Request.locale.language']); // $container->get('Request')->getLocale()->getLanguage()
public
– determines if the service should be accessible directly with get
, or can it only be injected into other services.
Usually it’s obvious, which service should be injected into another. For instance when your service has a constructor argument Psr\Cache\CacheItemPoolInterface $cache
, and the container does have a service named Psr\Cache\CacheItemPoolInterface
, then explicitly writing ['arguments' => ['$cache' => '@Psr\Cache\CacheItemPoolInterface']
is redundant. You can always specify the dependencies manually, then autowiring won't overwrite them.
Autowiring is not magic – it just follows simple rules to determine, which service should be injected into the constructor:
s
(e.g. array $helpers
), inject an array of services with a specific tag (#helper
).Bag
, inject the config value with its name (e.g. Bag $localisation
-> @config.localisation
),env
, inject a parameter: (e.g. string $envCacheDir
-> %CACHE_DIR%
)Autoconfiguration is another way to make your life simpler. For instance, if you’re using Twig, you might want all the classes in your code that extend Twig\Extension\AbstractExtension
to be automatically registered as twig extension. Autoconfiguration lets you define what default config (tags, public etc.) should be added to them.
To use autowiring and autoconfiguration, run the ContainerCompiler
:
$container = new Container;
$services = [
'App\' => [
'dir' => '%MODULE_DIR%/src/',
'exclude' => ['#^Entity/#'],
],
'App\Foo' => [
'arguments' => [
'$bar' => 5,
],
],
'App\Bar' => [
'public' => true,
],
];
$autoconfiguration = [
'Twig\Extension\AbstractExtension' => [
'tags' => ['twigExtension'],
],
];
$definitions = new ContainerCompiler(
$container,
$services,
$autoconfiguration
))->compile();
/** @var ServiceDefinition $definition */
foreach ($definitions as $name => $definition) {
if (!$container->has($name)) {
$container->setDefinition($name, $definition);
}
}
In this example, the whole %MODULE_DIR%/src/
except for (the /src/Entity
dir) will be scanned for PHP files and all the found classes will be autowired as private services. If some of them are not used and not public, they will be removed from the container.
Compiling the container has no impact on performance on production environment, as long as you cache the result of compile()
.
Service locator restricts access to services in the container only to a selected list of names:
$container = new Container();
$container->set('foo', 'abc');
$container->set('bar', 'def');
$container->set('secret', 'XYZ');
$locator = new ServiceLocator($container, ['foo', 'bar']);
$locator->get('foo'); // 'abc'
$locator->get('bar'); // 'def'
$locator->get('secret'); // Exception
ContainerAssistedBuilder
can be used to join together a couple of ContainerBuilderExtension
s which encapsulate a set of service definitions that form a library together. For an example, see Avris Localisator.
This container was originally built as a part of the Micrus framework.
]]>Having 100% of LOC covered by unit tests certainly feels like a great achievement. But beware – that doesn’t necessarily mean your code is perfectly covered. Lines of code coverage is a really nice indicator of your app’s stability, but is can also hide some risks.
Let’s say you have simple method to test:
public function calculateStuff(): int
{
if ($this->debug) {
// do some stuff
return $result;
}
return 0;
}
How many test cases should you write for it? Obviously, two: one to check for a valid output if debug
is true, and one when it’s false. If you only write one of them, LOC coverage will make it clearly visible that something is missing. But what if your code looks like this?
public function calculateStuff(): int
{
return $this->debug ? $this->calculateResult() : 0;
}
You still have exactly the same two logic paths, but only one line of code. Testing any of those paths will result in the entire method being “covered”.
And what about this code?
public function onException(ExceptionEvent $event):
{
if (!$this->debug) {
$event->setResponse($this->renderErrorPage());
}
}
If you first test the case when $debug === false
, then grab some lunch, and then come back not remembering the details, but seeing a 100% coverage – will you remember to also test the other case?
Summing up: having the tests execute every single line of your code still does not guarantee that all the logical paths of your class are being executed.
Also: having all the logical paths executed doesn’t necessarily mean you got them tested.
class Foo
{
public function run($input)
{
// lots of complex logic
// write the result to a file
return $output;
}
// lots of private methods
}
class FooTest extends TestCase
{
public function testRun()
{
$foo = new Foo;
$this->assertEquals(8, $foo->run(123));
// other, similar test cases
}
}
It’s very possible this test will cover every single line of those lots of complex logic
. But what about the side effects? We are writing something to a file after all (a log maybe?). We are testing that the output is correct for a given input, but we forgot to test that the side effects are also working as expected.
The nicest would be, of course, to avoid any side effects and code more functional-programming-style, but that might not always be doable easily enough. So the second best thing would be this: don’t trust your coverage, check if what was covered, was also tested.
It’s virtually impossible to mock all the dependencies away. Well, ok, it’s possible, but sometimes it’s more effort than it’s worth. Most of the time when you’re testing class X
, some lines of classes Y
and Z
will also get executed. Usually, it’s harmless. But sometimes it could blow up in your face – you could get the entire class Y
“covered” without actually testing it. It’s then very tempting to just leave it as it is (100% achieved, why would I even bother?) and very simple to just miss it.
That’s why it’s a great habit to tell the testing framework, what are you testing with this specific case. In PHPUnit you can do it using the @covers
annotation. If you specify that XTest
@covers X
, PHPUnit will still execute the code of Y
and Z
, but will not count that as a coverage of Y
and Z
.
Let’s not kid ourselves – the test cases for more complex applications are never perfect in the sense of covering every single scenario that your code might run into. It’s just not worth the effort, “good enough” is usually good enough.
Still, having high coverage is a good indicator. The higher it is, and the more meaningful it is (in terms of what I wrote above) – the safer you can feel when refactoring your code, or modifying it in any other way.
But getting to 100% is not easy.
Follow clean code rules and use design patterns. If you repeat yourself ( DRY), you’ll have to repeat the tests as well. If your class has multiple responsibilities ( SRP), it becomes considerably more difficult to test it. Testing (A+B) together can sometimes be way way harder than testing A + testing B.
Use inversion of control, like DI. If your class creates its dependencies or fetches them as a singleton, it’s almost impossible to reliably mock those dependencies away. If you just pass them in the constructor though – it’s an easy peasy to just pass a mock instead of an actual object.
The simpler your code, the easier it is to test it. When writing tests for Micrus v4.0, I was surprised how much has my coverage grew not by adding new tests but by removing some not-so-useful, outdated or too complex code.
There is a really fancy and clever library for PHP, AspectMock, that uses the power of aspect oriented programming to make everything testable. Static methods, final classes, global system functions, dependencies created inside of the tested class – everything. It basically modifies the source code in run-time, letting you do anything with it.
Awesome as it may be, I’ve decided to move away from it. Micrus v4.0 doesn’t use it all, even though in v3.0 it was a basis for every single mock. Why?
It’s quite an overhead, both in terms of speed and the time spent to configure it (best case scenario is very simple, but as soon as anything is misconfigured, debugging becomes a nightmare).
But most importantly: it lets you get away with ugly code. Why would you care about passing the dependencies in the constructor, if AspectMock lets you mock all the anti-patterns you can think of? Why would you care about making your code testable (= simple), if everything is testable?
If you depend on a legacy code, AspectMock might be your only chance to test some parts of it at all. However, if you’re writing something modern – I’d recommend avoiding this kind of magic.
How to achieve 100% coverage without resolving to AspectMock or similar magic? I’ve got some advice more specific than just SOLID or DI (although they are essential).
Most importantly: mocking the global functions. That’s the feature for which I was considering staying with AspectMock after all. Then I found this article, showing how to achieve a similar result without that much overhead. It wasn’t easy to use and re-use though, so I decided to write a solution which is. That’s how Avris FunctionMock was born.
Basically it lets you write something like:
FunctionMock::create(__NAMESPACE__, 'time', 123456789);
and it will make all the calls to time()
inside __NAMESPACE__
return 123456789
regardless of what the actual time is. You can also use a callback, disable a mock and validate how many times and with what arguments was the mock invoked.
When it comes to the issue of accessing private/protected methods/properties (which you shouldn’t do, but sometimes have to), reflections come to the rescue:
$r = new \ReflectionProperty(Application::class, 'commands');
$r->setAccessible(true);
$commands = array_keys($r->getValue($consoleApp));
$this->assertEquals(['help', 'list', 'test'], $commands);
or
$r = new \ReflectionMethod(FillEnvCommand::class, 'getDefaults');
$r->setAccessible(true);
$defaults = iterator_to_array($r->invoke($command));
If you’re being nice and keep your classes final
(to enforce Composition over inheritance), you should also be nice and make them implement an interface and depend on that interface ( Dependency inversion principle). You can’t mock a final class – but you can mock that interface.
And when it comes to mocking static methods – if you need to mock them, you’re doing something wrong. Static methods should be avoided, except for things like factories etc.
Having high code coverage is nice, but remember it might give you a false feeling of security. Don’t rely simply on that one number. And that’s basically all I wanted to say 😁
]]>But if it’s not a black magic, not a secret knowledge, then why are software developers so well paid?
It all comes down to the issue of maintainability. It’s not that hard to write a script that works. But it requires a lot of experience to write something, which can be easily modified.
Programmers spend more time reading code than writing it. Seriously. Sometimes we spend the whole day digging through layers of code in search of a root cause of some bug, only to discover that all you need to fix it is to slightly modify a single line of code. (Btw, that’s one of the reasons one shouldn’t pay programmers per line of code).
When I look at some code I wrote just four years ago, I’m terrified of it. If I had to modify something non-trivial on that website, I think I would have to get drunk first. It’s a mess. No separation of concerns, no layers of abstraction, sensitive configs in PHP files, templates mixed up the the business logic – not only is it objectively ugly and unreadable, but most importantly, it takes a lot of time to understand what the code does, how it does it and how to change it best.
There is also a lot of duplication. I wrote some code to handle a post form in the admin panel. Then copy-pasted it and adjusted to handle an image form. Then the same for a comment form. And some others. Now if I had to change something regarding the form handling generally, I’d have to do it in all of those places (knowing the whole application, and trying not to forget/miss any of them). If only I had that shared logic separated into any kind of abstraction, I would only have to update that abstraction, so that every form which uses it would just switch to the new version.
That code would also cause me trouble regarding cooperation. Not only because I would be ashamed to share my shitty code with anyone. Mostly because the experience of my shitty code would be even more shitty for someone who didn’t write it. If I had written cleaner code, followed styling conventions and SOLID principles, wrote automated tests, used an MVC framework – then every experienced developer could just jump into the project without much preparation, knowing where to look without having to know the whole project. But in case of my shitty code, they would have to suffer through a lot before being able to work on it. Plus, they’d have to know Polish – for some reason I used it to name some variables and templates... 🤦
That’s why I’m distinguishing between coders and developers. When I started working as a programmer, I was just a coder – I was simply able to write my ideas down as a code, to tell the computer what I want it to do. Over the years I’ve gathered the mindset and the skillset of a developer – a person that, well, develops a product. From start to end, from the architecture design to unit tests, able to communicate efficiently with others who work on it (developers, project managers, designers etc.), writing a code that’s easy for others to co-create and maintain. Coding is more about knowing a tool, developing is more about the mindset. I’m able to switch to a different framework or a completely different language within a week or two – because those are just tools.
So to sum up: of course you can hire some intern for 10 PLN/hour – they will take a month to create something, but if they’re good, they will get shit done and working just fine. However, if you want to maintain this code later, develop your product further, add more people to your team, fix some bugs that came up – it will take a lot of time, tech debt will rise, bugs will multiply, time needed for testing will become huge. And you could also hire someone for 30 EUR/hour – who would create the same functionality in just a week, keeping the code clean, easy to maintain, up-to-date with the newest trends in the field, interoperable and easier to test.
And that’s why I get so pissed when I hear stuff like “my niece also does websites, she would do it for a lollipop, and you want money from me?” or “I had some programming in high school, looks easy, you just google stuff and copy answers from Stack Overflow, how dare you make the money you do!”.
Well, no. Sure, programming is basically just splitting complex problems into a set of simple instructions, so that even something dumb like a computer can understand and execute them – in principal it really is easy. But when it comes to doing it right – that’s where software development becomes a real challenge.
]]>Finally. I got to work and rewrote the code of my sweet blog. Brand new design, new framework, Micrus, better support for language versions, a couple of new features in the admin panel, ditching custom comments for the awesomeness of Disqus, ditching TinyMCE for the beauty and simplicity of Markdown. It was a lot of work, but it was definitely worth it!
Hope you like it! :)
]]>Wreszcie. Wziąłem się do roboty i przepisałem od zera kod mojego blogaska. Zupełnie nowy design, nowy framework, Micrus, lepsze wsparcie dla wersji językowych, parę nowych ficzerów w panelu administracyjnym, rzucenie własnego systemu komentarzy na rzecz zajebistości Disqusa, rzucenie TinyMCE dla piękna i prostoty Markdownu. Zajęło to sporo pracy, ale zdecydowanie było warto!
Mam nadzieję, że się spodoba! :)
]]>How big does a framework need to be to provide you with a quick, easy and comfortable way of creating neatly structured MVC websites? That can easily be extended and configured?
Well, not big at all. Just try Micrus! Its goal is to keep it as simple as possible, while offering all the most important features, as listed here:
Jak duży musi być framework, aby umożliwiał szybkie, łatwe i wygodne tworzenie ładnie uporządkowanych stron MVC? Aby był łatwo rozszerzalny i konfigurowalny?
No właśnie wcale nie tak duży. Wypróbuj Micrusa! Jego celem jest bycie tak małym, jak to tylko możliwe, lecz oferować wszytkie najważniejsze funkcjonalności:
I stumbled upon a company that rewards their developers for the number of committed lines of code, and generally for the number of commits. What an utterly idiotic idea!
The thing is, it’s not that hard to write a code that works. It’s not hard to write a lot. With enough hard work and enough attempts everybody can achieve it. But the true skill, and the reason that qualified programmers are so well-paid, lies in the ability to write good code. Clear, simple, short, without duplication, understandable without any comments or introductions.
Writing some repeatable code in ten places makes you earn 10x more LOC than just wrapping it in a reusable function. But if you ever need to come back to that code, understand it and modify it, having it abstracted to a function makes it 10x faster and easier.
Good design and maintainability are worth so much more than just a sheer number of lines.
Another example: every serious developer knows that dependencies shouldn’t be included in the repo. We have Composer, Bower, NPM, Gem and so many others to handle the dependencies! But imagine your boss pays you for LOC, and you could “earn” millions of them just by commiting all the external libraries to the repo... Could you resist?
In the and, the “most active” programmer in that company turned out... not to be a programmer at all! He just set up all the new projects with bootstrap code. Surprise, isn’t it?
]]>Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. [source]
Picco is a tiny PHP web framework that only takes ~2,5 kB of space and has no dependencies on other libraries, while still providing quite a lot of features, being extensible and reasonably easy to use.
]]>The idea is to create a simple (in terms of complexity, not ease of use) programming language, that will have quite a concisesyntax, that will play with the power of Unicode and most importantly – will be fun to create ;)
]]>Missing your cosy home? Come in to find out, if you can stop working already and head back home!
]]>