10 changed files with 247 additions and 4 deletions
@ -0,0 +1,37 @@ |
|||||||
|
+++ |
||||||
|
title = "Things I Learnt The Hard Way - The Config File Is Friend" |
||||||
|
date = 2019-07-15 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["en-au", "books", "things i learnt", "configuration", "config file"] |
||||||
|
+++ |
||||||
|
|
||||||
|
Do not ignore the power of configuration files. |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
Imagine you wrote a function that you have to pass a value for it to start |
||||||
|
processing (say, a twitter user account id). But then you have to do that with |
||||||
|
two values and you just call the function again with the other value. |
||||||
|
|
||||||
|
It makes more sense to use a config file and just run the application twice |
||||||
|
with two different config files 'cause, this way, you have a single, small, |
||||||
|
testable application instead of two, or a very complex application that does a |
||||||
|
lot of stuff. |
||||||
|
|
||||||
|
We can even jump into the idea of [creating |
||||||
|
libraries](/books/things-i-learnt/libraries) and say that, instead of |
||||||
|
splitting your e-commerce application into smaller parts and making a big one |
||||||
|
by grouping these smaller parts, you could simply have one e-commerce |
||||||
|
application and, for each of your clients, you would have a different |
||||||
|
configuration file, pointing to different assets. This way, even the assets |
||||||
|
may reside in the same repository in the same branch, 'cause all that |
||||||
|
identifies which assets should be used are defined in the configuration file. |
||||||
|
|
||||||
|
"But which one should I use?" you may ask. Well, "it depends". It may make |
||||||
|
sense to have one single application with different configuration files if |
||||||
|
most of its can be used all the time. If the intersection of used things is |
||||||
|
very small, it may make more sense to split into different libraries and just |
||||||
|
"pick and chose" what to use. |
||||||
|
|
||||||
|
{{ chapters(prev_chapter_link="/books/things-i-learnt/libraries", prev_chapter_title="Create Libraries", next_chapter_link="/books/things-i-learnt/throw-away", next_chapter_title="Be Ready To Throw Your Code Away") }} |
@ -0,0 +1,48 @@ |
|||||||
|
+++ |
||||||
|
title = "Things I Learnt The Hard Way - Create Libraries" |
||||||
|
date = 2019-07-15 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["en-au", "books", "things i learnt", "libraries", "project organization"] |
||||||
|
+++ |
||||||
|
|
||||||
|
One thing you must learn is how to break your project into smaller libraries, |
||||||
|
to avoid doing rounds to deal with "the same, but a bit different". |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
I've seen a lot of projects that use things like branches for different |
||||||
|
things. Say, you have an e-commerce page. But you also have different clients, |
||||||
|
and they all have different colours and logo. Some people would take this |
||||||
|
scenario and, using the VCS properties, use the main branch for the main code |
||||||
|
and a branch for each client, merge from main branch from time to time -- and, |
||||||
|
thus, the branches are never merged back. |
||||||
|
|
||||||
|
This is suboptimal, 'cause that's not how VCS are supposed to be used. |
||||||
|
|
||||||
|
But you can, for example, break the main code into a library/framework and |
||||||
|
have one project for each client, with their assets and you just reference the |
||||||
|
library/framework in each. |
||||||
|
|
||||||
|
Simple and clean. |
||||||
|
|
||||||
|
But stop there for a second. Although this makes the code cleaner, avoids |
||||||
|
duplication and uses a VCS in the way it was supposed to be used, you can't |
||||||
|
start this way. |
||||||
|
|
||||||
|
Remember that [future thinking is future |
||||||
|
trashing](/books/things-i-learnt/future-trashing). What you can do is actually |
||||||
|
break your project by functionality, [making modules related to their |
||||||
|
data](/books/things-i-learnt/project-organization) and then, when you get a |
||||||
|
reasonable number of clients, you'll notice what can be reused in each, what |
||||||
|
modules make sense for one client and not for another. And then you'll have a |
||||||
|
good way to deal with those. |
||||||
|
|
||||||
|
One project that may appear when creating libraries is "How do I create my own |
||||||
|
library repository?" 'Cause all modern languages today have support for |
||||||
|
importing external libraries and, even if your libraries will never be out of |
||||||
|
your control, they are external to the project. So you may need to learn how |
||||||
|
to deal with this before creating the libraries. And, unfortunately, each |
||||||
|
language and build tool has its own way to manage this. |
||||||
|
|
||||||
|
{{ chapters(prev_chapter_link="/books/things-i-learnt/project-organization", prev_chapter_title="Organize Your Code by Data/Type, Not Functionality", next_chapter_link="/books/things-i-learnt/config-file", next_chapter_title="The Config File Is Friend") }} |
@ -0,0 +1,29 @@ |
|||||||
|
+++ |
||||||
|
title = "Things I Learnt The Hard Way - Logs Are For Events, Not User Interface" |
||||||
|
date = 2019-07-15 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["en-au", "books", "things i learnt", "monitoring"] |
||||||
|
+++ |
||||||
|
|
||||||
|
On a previous life, to understand how a system behaved, I added a ton of |
||||||
|
metrics: how fast things were going in, how fast things were going out, how |
||||||
|
many things were in the middle, how many the job processed... Not doing it so |
||||||
|
makes me feel... naked. |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
Monitoring your project performance give you a really good view of how a |
||||||
|
system is behaving. Is the speed going down? Is the system taking longer to |
||||||
|
process an input? Are no inputs being processed? |
||||||
|
|
||||||
|
If you have this kind of information, you can check what is going on in the |
||||||
|
system and understand why. Is it normal? Did a change around the system (the |
||||||
|
other system that produces the input, the system that consumes in the output) |
||||||
|
affected the results? |
||||||
|
|
||||||
|
If you're not measuring, you'll have no idea. |
||||||
|
|
||||||
|
Also, "If you can not measure it, you can not improve it", as Lord Kevin said. |
||||||
|
|
||||||
|
{{ chapters(prev_chapter_link="/books/things-i-learnt/log-events", prev_chapter_title="Logs Are For Events, Not User Interface", next_chapter_link="/books/things-i-learnt/languages-are-more", next_chapter_title="A Language Is Much More Than A Language") }} |
@ -0,0 +1,39 @@ |
|||||||
|
+++ |
||||||
|
title = "Things I Learnt The Hard Way - One Commit Per Change" |
||||||
|
date = 2019-07-09 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["en-au", "books", "things i learnt", "source control", "commits"] |
||||||
|
+++ |
||||||
|
|
||||||
|
When working with source control tools, keep one change per commit. Avoid |
||||||
|
bundling more than one change in a single commit just to "save time". |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
I've seen my fair share of commits with messages like "Fix issues #1, #2 |
||||||
|
and #3". This is not something you should do. One commit for fixing issue #1, |
||||||
|
another for #2 and yet another for #3. |
||||||
|
|
||||||
|
Just note that I said "one commit per change", not "one commit per file". |
||||||
|
Sometimes, to make a single change, you may need to change more than one file |
||||||
|
-- it may point that you have a coupling problem, but that's a different |
||||||
|
issue. You could, for example, make one commit which adds a new field in model |
||||||
|
without adding a change in the controller to load this field; after all, the |
||||||
|
controller won't (or, at least, shouldn't) break due the added field, and the |
||||||
|
model won't break (or, at least, shouldn't) break because the controller is |
||||||
|
not touching the field[^1]. |
||||||
|
|
||||||
|
When making a commit, think this: "In case something goes wrong, can I undo |
||||||
|
this commit without breaking other stuff?" Commit history is stacked, so |
||||||
|
obviously you'd have to undo the commits on top of that one. And that's |
||||||
|
alright. |
||||||
|
|
||||||
|
**BONUS TIP**! If you're using `git`, you can use `git add -p` in case you |
||||||
|
"overchange". It will allow you to pick parts of a file, instead of adding all |
||||||
|
the changes in the file before committing. |
||||||
|
|
||||||
|
[^1]: Ok, it _may_ have some issues if the field can't be null, but you get |
||||||
|
what I meant, right? |
||||||
|
|
||||||
|
{{ chapters(prev_chapter_link="/books/things-i-learnt/always-vcs", prev_chapter_title="Always Use A Version Control System", next_chapter_link="/books/things-i-learnt/project-organization", next_chapter_title="Organize Your Code by Data/Type, Not Functionality") }} |
@ -0,0 +1,84 @@ |
|||||||
|
+++ |
||||||
|
title = "Things I Learnt The Hard Way - Organize Your Code by Data/Type, Not Functionality" |
||||||
|
date = 2019-07-15 |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["en-au", "books", "things i learnt", "project", "project organization"] |
||||||
|
+++ |
||||||
|
|
||||||
|
A lot of projects assume that you'll put things with the same functionality in |
||||||
|
the same place, no matter what data they deal with. This makes things harder |
||||||
|
to break apart later. |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
Most projects keep organized by the functionality each component do. For |
||||||
|
example, all the models are in the same place, all the functions that convert |
||||||
|
one model into an internal structure/DTO are kept together, and so on. |
||||||
|
Something like this: |
||||||
|
|
||||||
|
``` |
||||||
|
. |
||||||
|
+-- IncomingModels |
||||||
|
| +-- DataTypeInterface |
||||||
|
| +-- DataType1 |
||||||
|
| +-- DataType2 |
||||||
|
| +-- DataType3 |
||||||
|
+-- Filters |
||||||
|
| +-- FilterInterface |
||||||
|
| +-- FilterValidDataType2 |
||||||
|
+-- Processors |
||||||
|
| +-- ProcessorInterface |
||||||
|
| +-- ConvertDataType1ToDto1 |
||||||
|
| +-- ConvertDataType2ToDto2 |
||||||
|
+-- OutgoingModels |
||||||
|
+-- DtoInterface |
||||||
|
+-- Dto1 |
||||||
|
+-- Dto2 |
||||||
|
``` |
||||||
|
|
||||||
|
This is fine and works. But when you organize by data, it'll make a lot easier |
||||||
|
to split your project in smaller projects -- 'cause, at some point, you may |
||||||
|
want to do almost the same thing as you're doing right now, but with small |
||||||
|
differences. |
||||||
|
|
||||||
|
``` |
||||||
|
. |
||||||
|
+-- Base |
||||||
|
| +-- IncomingModels |
||||||
|
| | +-- DataTypeInterface |
||||||
|
| +-- Filters |
||||||
|
| | +-- FilterInterface |
||||||
|
| +-- Processors |
||||||
|
| | +-- ProcessorInterface |
||||||
|
| +-- OutgoingModels |
||||||
|
| +-- DtoInterface |
||||||
|
+-- Data1 |
||||||
|
| +-- IncomingModels |
||||||
|
| | +-- DataType1 |
||||||
|
| +-- Processors |
||||||
|
| | +-- ConvertDataType1ToDto1 |
||||||
|
| +-- OutgoingModels |
||||||
|
| +-- Dto1 |
||||||
|
... |
||||||
|
``` |
||||||
|
|
||||||
|
Now you can make a module that deals _only_ with Data1, another that works |
||||||
|
only with Data2 and so on. And then you can break them into isolated modules. |
||||||
|
|
||||||
|
And then when you have another project that also have Data1 but also deals |
||||||
|
with Data3, you can reuse most of the stuff in the Data1 module. |
||||||
|
|
||||||
|
And I do understand that this creates an explosion of directories/packages, |
||||||
|
which may seem a bit unnecessary. |
||||||
|
|
||||||
|
Believe me, I also thought the idea of keeping things by functionality made |
||||||
|
more sense. But in one project, I got a requirement to do almost the same |
||||||
|
thing as I was doing before, but with a small change, which would require one |
||||||
|
less step/transformation (in our example, you can think as the new requirement |
||||||
|
as doing exactly what the Data1, Data2 and Data3 did, with their |
||||||
|
transformations and such, but without the Data3 part). By breaking by their |
||||||
|
types, I managed to create small modules for each one and the new project |
||||||
|
would simply reference Data1 and Data2, but not Data3. |
||||||
|
|
||||||
|
{{ chapters(prev_chapter_link="/books/things-i-learnt/one-change-commit", prev_chapter_title="One Commit Per Change", next_chapter_link="/books/things-i-learnt/libraries", next_chapter_title="Create Libraries") }} |
Loading…
Reference in new issue