Julio Biason
5 years ago
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