12/27/2018

How to write a really object oriented program

Introduction

As I started to do interviews with new candidates to our team I just realised that object oriented programming is topic which is really not clear for most of the people. For the question: “Do you know what is object oriented programming?” I usually got the answer: yes. For the follow up question: “Could you shortly tell me what is it?” I never got twice the same answer. I got a lot of good answers and a lot of answers which were not really matching with my understanding. And at a point I started to think about how I would answer this question and I realised that it is not easy at all.
There was an other example: I needed to reuse an already existing C++ code for my component. It was basically one class with around 200 functions and 100 variables, all of them were public and it was pretty unclear what they are standing for. After an analysis I just figured out that I can not reuse it as it is, because of multiple reasons:
  • it was using a third party logger, which I did not want to include to my project
  • The program which was using it originally were based on TCP/IP communication. Most of the functions were throwing exceptions if the TCP communication was not set up properly even if they had nothing to do with that
  • Some of the member variables were used in multiple functions with totally different purposes. So if you wanted to call a member function from outside first you needed to set up ca. 10 member variables to the correct values to be able to get some results from the function.
All things considered I just decided to refactor this piece of code before reusing it and it took me a long time to figure out how to do it, because a lot of functions needed to be decoupled to a separate class and it needed to be figured out what effects are made by each variable for the different functions. It was a nightmare. As the customer asked me why this refactoring was needed I just replied: “Because it was not implemented in an object oriented way.”. At this point he told me: “Why not, it was all implemented inside a class, so it was object oriented.”. OK, from technical point of view it was implemented inside a class, so it was object oriented, on the other hand it was not following anything what means object oriented programming for me.
In this article I’d like to show you that object oriented programming is not just about using the related programming language elements, it is something much more than that. In my view you can also do object oriented programming in languages which are not supporting that officially (like C) and you can also work in a non object oriented way in pure OOP languages (like Java or C#). Let me show you my understanding about OOP.

Principles of Object Oriented Programming

First of all let’s see the official definition of OOP. I found a clear definition here: https://www.techopedia.com/definition/3235/object-oriented-programming-oop

It says:
Object-oriented programming (OOP) is a software programming model constructed around objects. This model compartmentalizes data into objects (data fields) and describes object contents and behavior through the declaration of classes (methods).

OOP features include the following:
  • Encapsulation: This makes the program structure easier to manage because each object’s implementation and state are hidden behind well-defined boundaries.

  • Polymorphism: This means abstract entities are implemented in multiple ways.

  • Inheritance: This refers to the hierarchical arrangement of implementation fragments.
Object-oriented programming allows for simplified programming. Its benefits include reusability, refactoring, extensibility, maintenance and efficiency.”
Let’s stuck first at the features:



  • Encapsulation: I think this is the most important one. Each of the objects has a state which is hidden for other objects. And on the the other hand there is an “implementation” for each object. I understand under that the member functions of the objects. And the state and the implementation together is one unit or with OOP terminology one object. So if your object is a rectangle it will have a state with elements like width, high, position, colour etc. And it will have an implementation, implemented member functions, like: move, resize, paint etc. And this is one object, not less, not more.

  • Inheritance: I would mention it before the polymorphism, because otherwise it is difficult to understand it. Inheritance stands for the connection between parent/child classes alias base/inherited classes. So for each class you can define so called child classes which can do everything what the base class can to (has the same public methods and member variables), but it can overdefine the implementation of the methods and it can extend the functionality (introduce new member functions and member variables). For example you have a class for animals. Each animal can move itself. But how they can move themselves if different for each animal. So the different animals will be inherited from the Animal base class and will override the implementation of move method. Plus some animals have some extra skills, like a dog can bark. So you can define a bark method for the Dog class.

  • Polymorphism: This is exactly what I told about the move method of the animals. You can have for example a collection of animals and you can call the move method for each of them and you shouldn’t mind how it is implemented for the animal.
That’s the official theory. Let’s go further with some practical theory.

Objects and classes and the connections between them

From the name of object oriented programming we can see, that objects are the basic units behind this paradigm. The next important feature is the class. But what is the relationship between objects and classes: class is in fact a collection, a template for similar objects, for objects with the same methods and member variables. So if you are having a rectangle, then you will have a rectangle object which is an instance of Rectangle class. The rectangle class define that it needs to have a size and a position and the object holds it’s exact size and position. There are programming languages (like JavaScript) where it is not needed to define the class of an object. In this case objects are created based on so called prototypes.
So when you are planning an object oriented program first you always need to think about what kind of classes will you need in your program.
The other important question is: what are the connections between these classes.
There are 4 types of connections between classes:
  • Composition:
    That means that the connected class is belonging to our class. They have the same lifetime (they are created and deleted in the same time). Our class can not work without the connected class. Like if we have a class called House and a class called Wall, then most likely there will be composition between them.
  • Aggregation:
    Aggregation between class A and B means that class A has an instance of class B. Like Car has an Owner. But the owner can exist also without the car, and the car can exist without the owner, the owner can change any time.
  • Association:
Association between class A and B means class A is using an instance of class B. Usually this relation between the objects is just a short term relationship, only during the run of the specific function. For example you have a class and it has a Log function. But this Log function needs an instance of the Logger class as a parameter. This is a typical example for association.
  • Inheritance/Generalisation:
This is the relation type already mentioned earlier. It means class A is a special case of class B. It’s also called as child/parent class as well.
So the next to do is to decide what kind of relations are there between your classes. It’s good to do some graphical modelling for your classes to have a better overview. For that the class diagram of UML is a good tool. I would propose to always do a plan of your classes at least on a paper before starting to program.
There are two additional definition I wanted to clarify:
State of the object: In fact this built up based on the current value of all member variables. So if you are changing at least one member variable the state of your object is also changing.
Public interface of the class: This is all the methods and variables which are public, which are visible from outside of the class.

Based on that you can do a plan of your needed classes, but there are still some so called “best practices” in OOP programming.

SOLID

These best practises are called SOLID. This stands for the followings:
  • Single responsibility principle
    Each of your classes should have a simple clear responsibility. You should be able to describe the purpose of the class with one sentence without using the words “and” and “or”. So yes: huge classes are most of the cases against this principle.
  • Open/closed principle
    Your code should be open for extensions, but closed for modifications.
  • Liskov substitution principle
    This is maybe a bit more complicated. But it means, that each of your parent classes can be replaced with any of its child classes without braking the functionality. This is coming from something called “Design by contract”. I plan to write a separate article about this topic.
  • Interface segregation principle
    Your interfaces shall be small and clear and having one well-specified purpose. So you should avoid having interfaces which are returning a lot of data. So the public interface of your class needs to be well designed and easy to use. Make everything public is not a good practise.
  • Dependency inversion principle
    You should design your classes your classes in a way, that the classes which are dependencies of your class can be set through some setter functions or constructor parameters. So that you can change them to any subtype later on. So for example if you have a Logger class which is used to create log files and you are can change it though a setter function you can change your Logger between an XMLLogger, a JSONLogger or a SimpleLogger if all these classes are derived from the same Logger base class. It is also helpful if at unit testing you need to mock your dependencies.
Let me extend it with my own suggestions:
  • Always try to keep all member variables (so the state of your class) private and use setter and getter functions for more control
  • Always try to design a user friendly public interface for your class
  • Avoid the long parameter lists at your member functions, in that case always think about transforming some parameters to member variables if it really makes sense
  • Read about object oriented design patterns and try to use them always when it is possible

Object oriented programming in practise

Now after getting familiar with all theory I wanted to include a short practical example. This is about implementing a Canvas which can visualise different shapes, like rectangle, circle, triangle and star.
A not really object oriented solution would be to create one Canvas class with member methods like: drawRectangle, drawCircle etc. But this solution is not nice, it is not using the features of object oriented programming and it makes difficult to extend your code (it is against open/closed principle).
Based on that my proposal would be the following:

Create a Canvas class. It should have a collection of shapes (aggregation with Shape class). It should also have member methods like addShape for adding new shapes or deleteShape for deleting already added shapes. It should have a drawAll function which draws all added shapes. Shape is a base class which has a draw method. Each shape has a (composition) size and a position. Position is a class with attributes for x and y coordinates. Size has a with and a height attribute. Each specific shape (Rectangle, Triangle, Circle and Star) are child classes of shape. So that each of them has a Position, a Size and all of them should implement the draw function. With this solution it is pretty to easy to extend the program with new shapes (just implement new child classes) of change the representation of position to other coordinate system.

This modelled and visualised in a UML class diagram should be something similar:

Summary


If you are an experienced software developer maybe I did not tell you anything new, but based on my experience a lot of programmers have a non-clear understanding about this topic, so I really hope I could help a lot of them. Based on my experience with this understanding you can ensure a much better quality for your work.

12/20/2018

How to give feedback to your boss?

Most of the developers has some kind of feedback discussion with their boss. In some cases it is regular (every month, every half year etc.), in other cases it is only done if it has a purpose.

A lot of programmers have the feeling that these meetings are basically just getting the orders from their boss for the next time. But it should not be like that. It should be a bidirectional feedback session.

Of course your boss has some long term goals with the team and has some plans with you personally as well. These meetings are normally about these goals: giving you some personal goals which are supporting the long term goals of the team and giving you feedback if something is going wrong. It’s up to your boss, but sometimes this is the whole content of your meeting and nothing more. Luckily the good bosses know: without good and enthusiastic employees non of the team goals will be satisfied. That’s why good bosses are asking the employees as well what they are feeling what is their feedback. But to give a correct answer on these questions is not always easy. Some of the developers just telling that everything is great, even if it’s totally not true. Others are starting to complain about everything. These are not so effective strategies. In the first case your boss will never know your problems, so most likely they won’t change. In the second case after a lot of complaining your boss won’t take care about your problems.

But how to do it correctly?

First of all, let’s prepare yourself for the meeting. Think about the time period since your previous meeting and collect all the point what you have successfully done: achieved milestones, good customer feedback, achieving a small part of your goals defined with your boss, changing something what was a negative feedback last time. And think about the things what were not going well as well. If you missed something try to find the reason why (you were busy with some other tasks etc.). Next to that always try to think about that what was positive for you and what was negative for you in the last period.

On the meeting always let your boss start the discussion and never stop him/her talking, even if you strongly disagree with something. Always wait until you are directly asked. In the first part of the meeting always try present your results: always start with the success stories and after them mention some of your failed achievements. Always try to tell that you did you best, but unfortunately it was not enough. Like: “Unfortunately we were late at the delivery, we worked hard on the milestone, but a tricky technical issue came into the picture and it took a lot of effort to solve it. At the end we found a good solution, but it was unfortunately too late, hopefully next time it will be faster.”.

As next stage of your meeting try to tell your feedback to your boss: always mention some positive things what you like (good team, interesting tasks, flexibility etc.) and after that you can change to your negative feedback. Always try to tell them with a positive attitude and try to present your related ideas. For example: “Unfortunately we need to do too often over hours which I don’t really like. Of course I can understand that it is needed sometimes. I think it could be much better if we could extend our team with a new developer”. If a problem if coming back and back again you can be a bit more strict, you can tell that “this problem is there since months, I already told you multiple times and it is making me demotivated”. But important is: never make surprises to your boss, don’t be too aggressive if you are mentioning a problem for the first time.

Most likely your boss has some feedback for you as well and most of the cases it has some negative part as well. The important thing is: always take questions related the negative feedback, even if you are strongly disagree. “What do you exactly mean with this point?” “How could I do it better?” etc. And at the end just tell that “OK, I’ll try to change it”. Then your boss should be happy with this reaction, in fact he/she can’t tell anything against you after that.

At the end of the meeting most likely you will talk about your next goals. Your boss has most likely some ideas and probably you have some ideas as well. Let your boss present his own ideas first. After that you can answer in such a way: “ok, clear, I like this idea, however I think in this way it would be more effective”. And here you can present your own idea. Pay attention: always keep some parts from the suggestions of your boss, but always bring your own ideas with some good reasons. If you are doing this part well then your boss will think that you agreed his idea, but in fact it’s mainly your idea, which is totally different than his idea was at the beginning.

One more important thing: always try to document everything from these meetings, it can be helpful later on.

The next important point: if your boss is not organising such meetings with you, but you have a feedback you can always ask your boss for a feedback discussion. Your boss will think that you are highly motivated and that’s why you are asking for a feedback. But after his feedback you are free to present your feedback as well.

At last never forget: of course your workplace is important for you, but you are important for your workplace as well. If you don’t like something there always try to change it. And the first step of every change is to give a clear feedback!

12/12/2018

The big testing guideline

Introduction
Based on my experiences for a lot a of people there’s a big chaos in their head about software testing, sometimes even for programmers and project managers. That’s why I decided to write a post which is giving a rough overview about the different ways of software testing.
As I started my carrier I was working for a small start-up. There software testing meant, that someone is starting the software, does some random clicks and checks if something strange happens or not. Later on I changed to automotive business. In this field testing is really strict. But all what I learnt: the software itself determines the right testing process. It is up to that: how safety critical is your software, who will use it, how complex is it etc.
There are several ways of testing, these methods can be either automated or manual, functional and non-functional, black box, grey box or white box  and it can be on several different levels (unit testing, component testing etc.). For each project you need to find the most fitting way of testing. Of course more methods you are using at testing there’s a lower chance to keep bug in the software (and this chance will never reach zero), on the other hand of course testing takes time, so it costs money as well. That’s why you need to find the most fitting way to your project. Now I will try to introduce you to the different ways of testing.

Functional and non-functional tests

Functional tests

A test can be either functional or non-functional. Functional test is usually against the functional requirements. Here you are basically checking if the functionality of your software is the same as the requested behaviour. You need always take care about corner cases as well.

Non-functional tests

Non-functional testing is going against the non-functional software requirements, it can go against the stability of the software, it can test if the software is running under each requested environment (operation system, web browser, different hardware etc.). Security test belongs to this category as well which is checking if your software is secure enough, it can be especially important in case of web based applications. Load test belongs here as well, which is testing how your software is reacting in case of large number of requests. Here you can test also the runtime of your software in the most critical scenarios (profiling) or the memory consumption. It can be also a usability test, where you are testing with the target users if your software is user friendly enough.

Automated and manual tests

Automated tests

Automated tests are normally so called “coded” tests. They are implemented once and can be run anytime later. Classical example is the unit testing of your code with some test frameworks (GTest, JUnit etc.). Other automated tests can be implemented with some testing frameworks (like Selenium) or with script languages (Python is also popular in this field). The common point here is that you are providing some inputs to your program, or to a piece of your program, then you are letting your program run and finally you are comparing the results with the so called expected results. The automated tests should be deterministic. That means with the same peace of code they should always pass or always fail. The big advantage of automated testing is that you can run such tests after each change of your code base and you can check if the changes were not breaking your already working functionality. For this reason you can integrate them to the continuous integration system, which is running your tests after each new commit once.
Automated tests can also be implemented parallel with your code, so you don’t need to wait with the test development for an already done software.

Manual tests

Manual tests are tests which can be done manually. So you are manually starting the program, doing some predefined steps and checking its behaviour. It is good to do such tests for functionality that you don’t want to test frequently or where it is difficult to test it in an automated way. What is important here: manual tests also needs to be done based on a test plan what is describing the different tests scenarios with exact test steps and expected behaviour.

Test categories based on knowledge about code

Black box test

In case of a black box test you have totally no knowledge about the code. You are just like a simple user, you are trying out the different functionality of the software, without considering its implementation details. For black box test is good to have a tester, who has really know knowledge about the implementation details of the software (so who didn’t take part in its development). Black box tests are usually done against the software requirements, which also needs to be well documented to be able to define good test cases. In case of a black box test you also need to think like a user. A black box test can be both automated or manual test.

White box test

In case of a white box test you have access to the whole code base and you are considering it at your test case definition as well. For example you know what are the exact parameters of your function and you know, that in case of a negative parameter value your function has to throw an exception then you should define such a test case. The most typical white box tests are unit tests, but you can do white box tests on different levels as well. It can be both automated and manual test. So that also counts as a white box test if you as the developer of your code you are trying out with a debugger how your code is behaving in case of specific inputs. One important counter at white box testing is called code coverage: it means how big percet of the lines of your code are really running when you are running all your tests.

Grey box test

In case of grey test you have limited knowledge about the code. For example you know what are the components, which interfaces are used, what are the parameters of the interfaces, but you have no idea about that how the component is implemented.

Levels of testing

There are different levels of testing from testing a small piece of code to testing the whole system. Each levels has their own advantages and disadvantages. Normally the advantage of lower level tests is that they can run faster and can show the root cause of the bug more precisely. On the other hand there are bugs which can be detected only by higher level tests, because they are caused by the wrong cooperation of different software components. But in case of higher level tests it is difficult to tell at a failing test case where is the root cause of the bug and normally they are running longer.

Unit test

Unit test is the lowest level of testing. Unit test is testing one unit. A unit should be small enough, most of the cases it is a function or a class. In case of unit testing you are really testing this small peace of code. To avoid to be influenced by other units other units can be mocked. That means you are changing them with code peaces with some fake/dummy behaviour. For such reason there are several mock frameworks, like Mockito or GMock. To be able to mock you need to have a clear and modulable code architecture. One common methodology to make mocking easy is called dependency injection.
A good unit test is small, fast, independent and testing one small piece of the code. Usually unit tests are automated but they can also be manual.
One more important method is called Test based development, where you are basically implementing your unit test cases parallel with your code.
Unit testing is usually done by the developer of the code. On long term it is useful to find regressions.
A good unit test is showing the exact place of the bug in case of failure, so that no long debugging is needed to fix your code.

Component test

Component test is about testing a component of your code. If your code is modular enough and it is organised into components with clear interfaces you can test these interfaces separately as well. This is a good candidate for grey box test. So you know what are the interfaces to be used (for example in case of a library) and you know what you are expecting from these interfaces, but you don’t know what is their exact implementation.

Integration test

Integration test are for testing how several components are working together with each other. Integration test can also detect bugs which are cause by the incorrect cooperation/connection of the components.

System test

System test is going against your whole system, so you software running on its target hardware in its target environment. These test are usually done as final tests, before the publication of the software. These should be done at each software release. This is usually a black box test, can be both manual or automated. This level of testing is usually done by an independent test team.

Who should test?

Regarding the question who should do the tests there are several different opinion. In case of classical testing approaches the rule is usually that the tester and the developer should be two different person. So if the developer was thinking in a wrong way or had a wrong understanding of the functionality the tester could still find the bugs. I think this is a good approach in case of higher level tests and usually in case of black box tests.
In case of modern testing approaches like test based development the tester and the developer is often the same person. This is passing well to the agile approach where all team member should be able to take over any kind of tasks (testing, development, design etc.). This approach is making the testing a bit faster, but based on my experiences it is also not so effective.
I think the most safe way is the aggregation of the two way: implementation of unit tests by the developer and testing on higher level with black box tests by an independent team. But in case of all development it needs to be considered if it worth to take so much time for testing. In case of safety critical system like automotive development of development of an airplane software of course it makes sense. But in case of a web page or a game mobile app maybe it is not so important.

Testing in project lifecycle

In the classical development methodologies like waterfall of V-Model tests are coming clearly after the implementation. But the development of automated tests can be also done parallel with the development. At the places where such classical methodologies are followed the software is also done in a cyclic way. That means there are regular software releases and each released software should be tested. So until the development team is working on release N+1 the test team is testing the release N and is preparing automated tests for release N+1.
At agile development methodologies, like SCRUM or Kanban, test driven development is often followed. That means the unit tests and the implementation are being done parallel. What is important, that in case of agile development you can also do some higher level tests. You can handle it as a separate user story to implement such tests. Or it can also be that there’s a separate test team which is also working for example based on scrum and they are always testing the results of the development team some sprints later.
In case of both classical and agile approaches I think it is really important to integrate the automated tests into the CI system, so it is able to check regularly if the software is working properly. Of course in this case the tests needs to be also updated regularly up to the newly implemented requirements. Since to run all tests can take long it is good to setup your CI separately for the different levels of testing. Like run all unit tests in case of any new commit, run all component and integration tests every night and run all the tests at each software release.

Summary


I just realised during the writing that it is really a complex topic, so I think I couldn’t introduce everything detailed enough, but I hope I could give you a good overview. Since testing is done with different approaches and in different ways at different companies I’m almost sure that experienced tester would disagree with some of my points. It can happen, I’m also not a tester to be honest. I hope my article could help you.

12/07/2018

Being a father and a programmer in the same time

Before I became a father I had totally different priorities in my life, but at that point everything changed. I needed to find a balance in my life between my new and old responsibilities. I wanted to be a good father, a good husband and a good programmer at the same time. In fact it is not easy at all. I can imagine only one situation which could be even more difficult: being a mother and a programmer in the same time. But luckily I will never experience that. Of course it is the some for every father, also difficult for non-programmers, but in this post I would like to concentrate on the point which are specific for programmers. So this is a non-technical post for programmers.

Let’s see the facts. One hand if you want to be a good husband and father you need to be at home in time, to be able take as much time as possible with your family. And during this you shouldn’t be too tired or stressed, you need to be mentally there and remember on things which are not in your calendar, not pushed to a version control system and are not documented at all, that’s challenging.
On the other hand you need to take care about the financial needs of the family. So unfortunately skip work is not really an option.

To be able to achieve it at your work you need to be mental fit as well, you need to be available in case of stressful project situation and make sometimes over hours as well.

So to summarize, you will have two responsibilities: at the first one everything is deterministic, documented and you have long years experience with that, at the second one everything is totally random, undocumented and you have no idea how to do it. One thing is common: both of them means a lot of work and needs a lot of time to fulfill all the expectations.

What advantages do programmers have compared to other professionalities:

  • Opportunity for home office: always more and more companies are providing such an option at least 1-2 days per week. I really suggest you to choose such an employer
  • Flexible working hours: programming is normally not a job what you doing from 8 to 4. You have here a flexibility and a chance to find the most convenient time for work. Do it, find the most fitting one. I personally always try to arrive early and leave early to have more time after work.
  • Good salary: I think this point doesn’t need more explanation, but it is really important if you have a family
Of course there are several disadvantages as well, but think in a positive way and call them rather challenges:

  • Critical situations in your projects where over hours are requested: at most of the companies there are sometimes critical project period, when everything is burning and you need to work really a lot to achieve your goals. And of course it is very difficult to let the family understand such situations. Here you need to do some self control as well: do over hours only if they are really needed, but avoid them in all other cases. You can use some hybrid solutions as well: do extra work at night when the family is sleeping.
  • It is a mental challenge: programming is a mental challenge, I mean I’m quite often thinking about the solution of my current tasks even out of work: at home, during travelling etc. Based on my experiences it is really difficult to take care about a child while you are just figuring out the best algorithm for your current problem. You need to stop thinking about such topics when you are with your family, even if it is difficult. And during this time you need to load some other memory areas where you are storing info like, what should your child eat, what time do you have an appointment at the child doctor or where are the clothes of your child stored. For me it is more challenging then solving any of my daily programming tasks…
  • You need to develop yourself: for most of the programmers programming related activities are not limited to the workplace: you are doing home projects, learning new technologies at home, reading technical articles and forums, checking for new work opportunities or writing blog posts. These are things which can make your carrier and salary up, but if you have a family it is difficult to find time for such activities.
Finally I just collected my suggestions to find your work-life balance:
  • Family should be always prioritized over project and work: you can always find a new job or new project if things are going wrong, but it is almost impossible to find a new family.
  • Don’t do too many over hours: too many over hours just destroying your balance
  • You need to know how to say no: Sometimes you need to say no even for good opportunities if it is not fitting into your time. Never mind, an other one will come later.
  • It is never too early to start to teach programming to your children!
Let me share thought: always think about that what you wanna remember in 40-50 years: on the time you spent with your family or on the project which you were working on and set up your priorities based on that!

So all things considered it is not easy at all to do these two things well in the same time but is is quite challenging, good luck on that!

12/04/2018

How to write a bug report?

This one is not a real blog post, just something short what came into my mind and what I wanted to share with you.

Unfortunately I have really bad experience with the bug reports I'm receiving. I often receive things like this:

  • "The software is not working" => OK nice, what does it mean not working? Did you forget to start it?
  • "I can not click on the button" => Your mouse is not working or what?
  • "It is not saving the file"
In case of such bug reports it can take hours to figure out what you really meant and to reproduce it. So it is just a waste of time and you also need to wait longer for your bug fix.
Instead of that please try to specify all details regarding how to reproduce your bug and why is its behavior not normal?

A bug report should always contain the following information:
  • Which working environment do you have (hardware, which operation system, other relevant software (ex. browser in case of web application)) etc.
  • Which configuration/settings of the software are you using?
  • What are the steps to reproduce the issue (really from starting the software, please)?
  • What is the behavior you would expect here?
  • What is the current behavior (which is of course not the same as the expected one, that's why it's called bug)?
If you are reporting your bugs in such a way the developer who will work on that will be happier and your bug will be fixed faster!

Thanks!

How I prepared my first online course

Since long I didn't publish anything here. It's because I was busy with some other topics, but now it's time to share the result...