2024-01-21 11:48:28 +00:00
|
|
|
<!doctype html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
|
|
<link
|
|
|
|
rel="stylesheet"
|
|
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.0/github-markdown.min.css"
|
|
|
|
/>
|
|
|
|
<title>haunt98 posts</title>
|
|
|
|
</head>
|
|
|
|
<style>
|
|
|
|
.markdown-body {
|
|
|
|
box-sizing: border-box;
|
|
|
|
min-width: 200px;
|
|
|
|
max-width: 980px;
|
|
|
|
margin: 0 auto;
|
|
|
|
padding: 45px;
|
|
|
|
}
|
|
|
|
|
|
|
|
@media (max-width: 767px) {
|
|
|
|
.markdown-body {
|
|
|
|
padding: 15px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<body class="markdown-body">
|
|
|
|
<h2>
|
|
|
|
<a href="index.html"><code>~</code></a>
|
|
|
|
</h2>
|
|
|
|
<h1>
|
|
|
|
<a
|
|
|
|
id="user-content-backend-thinking"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#backend-thinking"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>Backend Thinking
|
|
|
|
</h1>
|
|
|
|
<h2>
|
|
|
|
<a
|
|
|
|
id="user-content-backend-role"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#backend-role"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>Backend Role
|
|
|
|
</h2>
|
|
|
|
<p>Transform business requirements to action, which usually involves:</p>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Service:
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
ZaloPay use microservices architecture, mostly written using Go and
|
|
|
|
Java
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
API:
|
|
|
|
<ul>
|
|
|
|
<li>HTTP (Client-Server) and GRPC (Server-Server)</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Database/Cache/Storage/Message Broker
|
|
|
|
<ul>
|
|
|
|
<li>MySQL/Redis/S3/Kafka</li>
|
|
|
|
<li>CRUD</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Docs
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Mostly design notes and diagrams which show how to implement
|
|
|
|
business requirements
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<p>After successfully do all of that, next step is:</p>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Testing
|
|
|
|
<ul>
|
|
|
|
<li>Unit tests, Integration tests</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Observation
|
|
|
|
<ul>
|
|
|
|
<li>Log</li>
|
|
|
|
<li>Metrics</li>
|
|
|
|
<li>Tracing</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-21 18:41:21 +00:00
|
|
|
<p>
|
2024-01-24 03:32:12 +00:00
|
|
|
In ZaloPay, each team has its own responsibilities/domains, aka many
|
2024-01-21 18:41:21 +00:00
|
|
|
different services.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
Ideally each team can choose custom backend techstack if they want, but
|
|
|
|
mostly boils down to Java or Go. Some teams use Python for scripting, data
|
|
|
|
processing, ...
|
|
|
|
</p>
|
|
|
|
<p>
|
2024-01-24 05:51:38 +00:00
|
|
|
<em>Example</em>: Team UM (User Management) has 10+ Java services and 30+
|
|
|
|
Go services.
|
2024-01-21 18:41:21 +00:00
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
The question is for each new business requirements, what should we do:
|
|
|
|
</p>
|
|
|
|
<ul>
|
|
|
|
<li>Create new services with new APIs?</li>
|
|
|
|
<li>Add new APIs to existing services?</li>
|
|
|
|
<li>Update existing APIs?</li>
|
|
|
|
<li>Change configs?</li>
|
|
|
|
<li>Don't do anything?</li>
|
|
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
<em>Example</em>: Business requirements says: Must match/compare user EKYC
|
2024-01-23 19:53:59 +00:00
|
|
|
data with Bank data (name, dob, id, ...).
|
2024-01-21 18:41:21 +00:00
|
|
|
</p>
|
|
|
|
<h2>
|
|
|
|
<a
|
2024-01-22 02:40:27 +00:00
|
|
|
id="user-content-technical-side"
|
2024-01-21 18:41:21 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-22 02:40:27 +00:00
|
|
|
href="#technical-side"
|
2024-01-21 18:41:21 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-22 02:40:27 +00:00
|
|
|
>Technical side
|
2024-01-21 18:41:21 +00:00
|
|
|
</h2>
|
|
|
|
<p>How do services communicate with each other?</p>
|
2024-01-23 17:56:31 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
|
|
|
id="user-content-api"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#api"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>API
|
|
|
|
</h3>
|
2024-01-21 18:41:21 +00:00
|
|
|
<p>
|
|
|
|
<strong>First</strong> is through API, this is the direct way, you send a
|
|
|
|
request then you wait for response.
|
|
|
|
</p>
|
|
|
|
<p><strong>HTTP</strong>: GET/POST/...</p>
|
2024-01-23 19:53:59 +00:00
|
|
|
<p><em>Example</em>: TODO: show API image</p>
|
2024-01-21 18:41:21 +00:00
|
|
|
<p><strong>GRPC</strong>: use proto file as constract.</p>
|
2024-01-23 19:53:59 +00:00
|
|
|
<p><em>Example</em>: TODO: show proto file image</p>
|
2024-01-21 18:41:21 +00:00
|
|
|
<p>
|
|
|
|
There are no hard rules on how to design APIs, only some best practices,
|
|
|
|
like REST API, ...
|
|
|
|
</p>
|
|
|
|
<p>Correct answer will always be: "It depends". Depends on:</p>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Your audience (android, ios, web client or another internal service)
|
|
|
|
</li>
|
|
|
|
<li>Your purpose (allow to do what?)</li>
|
|
|
|
<li>Your current techstack (technology limitation?)</li>
|
|
|
|
<li>Your team (bias, prefer, ...?)</li>
|
|
|
|
<li>...</li>
|
|
|
|
</ul>
|
|
|
|
<p>Why do we use HTTP for Client-Server and GRPC for Server-Server?</p>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
HTTP for Client-Server is pretty standard. Easy for client to debug, ...
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Before ZaloPay switch to GRPC for Server-Server, we use HTTP. The reason
|
|
|
|
for switch is mainly performance.
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-23 17:56:31 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
|
|
|
id="user-content-message-broker"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#message-broker"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>Message Broker
|
|
|
|
</h3>
|
2024-01-21 18:41:21 +00:00
|
|
|
<p>
|
2024-01-23 17:56:31 +00:00
|
|
|
<strong>Second</strong> way is by Message Broker, the most well known is
|
|
|
|
Kafka.
|
2024-01-21 18:41:21 +00:00
|
|
|
</p>
|
2024-01-23 17:56:31 +00:00
|
|
|
<p>Main idea is decoupling.</p>
|
|
|
|
<p>
|
|
|
|
Imaging service A need to call services B, C, D, E after doing some
|
|
|
|
action, but B just died. We must handle B errors gracefully if B is not
|
|
|
|
that important (not affect main flow of A). Imaging not only B, but multi
|
|
|
|
B, like B1, B2, B3, ... Bn, this is so depressed to continue.
|
|
|
|
</p>
|
|
|
|
<p>Message Broker is a way to detach B from A.</p>
|
|
|
|
<p>
|
|
|
|
Dumb exaplain be like: each time A do something, A produces message to
|
|
|
|
Message Broker, than A forgets about it. Then all B1, B2 can consume A's
|
|
|
|
message if they want and do something with it, A does not know and does
|
|
|
|
not need to know about it.
|
|
|
|
</p>
|
2024-01-23 19:48:42 +00:00
|
|
|
<h3>
|
2024-01-23 17:56:31 +00:00
|
|
|
<a
|
2024-01-23 19:48:42 +00:00
|
|
|
id="user-content-tip"
|
2024-01-23 17:56:31 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-23 19:48:42 +00:00
|
|
|
href="#tip"
|
2024-01-23 17:56:31 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-23 19:48:42 +00:00
|
|
|
>Tip
|
|
|
|
</h3>
|
2024-01-21 18:41:21 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Whatever you design, you stick with it consistently. Don't use different
|
|
|
|
name for same object/value in your APIs.
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Don't trust client blindly, everything can be fake, everything must be
|
|
|
|
validated. We can not know the request is actually from our client or
|
|
|
|
some hacker computer. (Actually we can but this is out of scope, and
|
|
|
|
require lots of advance work)
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
Don't delete/rename/change old fields because you want and you can,
|
|
|
|
please think it through before do it. Because back compability is very
|
|
|
|
hard, old apps should continue to function if user don't upgrade. Even
|
|
|
|
if we rollout new version, it takes time.
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-23 17:56:31 +00:00
|
|
|
<p>
|
|
|
|
<strong>Pro tip</strong>: Use proto to define models (if you can) to take
|
|
|
|
advantage of detecting breaking changes.
|
|
|
|
</p>
|
2024-01-23 19:48:42 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
2024-01-23 19:53:59 +00:00
|
|
|
id="user-content-references"
|
2024-01-23 19:48:42 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-23 19:53:59 +00:00
|
|
|
href="#references"
|
2024-01-23 19:48:42 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>References
|
|
|
|
</h3>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/"
|
|
|
|
rel="nofollow"
|
|
|
|
>Best practices for REST API design</a
|
|
|
|
>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a href="https://docs.zalopay.vn/v2/" rel="nofollow">ZaloPay API</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://stripe.com/docs/api" rel="nofollow">stripe API</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://docs.moov.io/api/" rel="nofollow">moov API</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://blog.cloudflare.com/using-apache-kafka-to-process-1-trillion-messages/"
|
|
|
|
rel="nofollow"
|
|
|
|
>Using Apache Kafka to process 1 trillion inter-service messages</a
|
|
|
|
>
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-22 02:40:27 +00:00
|
|
|
<h2>
|
|
|
|
<a
|
|
|
|
id="user-content-coding-principle"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#coding-principle"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>Coding principle
|
|
|
|
</h2>
|
2024-01-23 19:48:42 +00:00
|
|
|
<p>
|
|
|
|
You should know about DRY, SOLID, KISS, Design Pattern. The basic is
|
|
|
|
learning which is which when you read code. Truly understand will be
|
|
|
|
knowing when to use and when to not.
|
|
|
|
</p>
|
|
|
|
<p>All of these above are industry standard.</p>
|
2024-01-24 03:32:12 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
2024-01-24 04:38:03 +00:00
|
|
|
id="user-content-write-code-that-is-easy-delete"
|
2024-01-24 03:32:12 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-24 04:38:03 +00:00
|
|
|
href="#write-code-that-is-easy-delete"
|
2024-01-24 03:32:12 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-24 04:38:03 +00:00
|
|
|
>Write code that is easy delete
|
2024-01-24 03:32:12 +00:00
|
|
|
</h3>
|
2024-01-23 19:48:42 +00:00
|
|
|
<p>
|
|
|
|
The way business moving is fast, so a feature is maybe implemented today,
|
|
|
|
but gets thrown out of window tomorrow (Like A/B testing, one of them is
|
|
|
|
chosen, the other says bye). So how do we adapt? The problem is to detect,
|
|
|
|
which code/function is likely stable, resisted changing and which is
|
|
|
|
likely to change.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
For each service, I often split to 3 layers: handler, service, repository.
|
|
|
|
</p>
|
|
|
|
<ul>
|
|
|
|
<li>Handler layer: Handle HTTP/GRPC/Message Broker/...</li>
|
|
|
|
<li>Service layer: All rules, logic goes here.</li>
|
|
|
|
<li>
|
|
|
|
Repository layer: Interact with cache/databases using CRUD and some
|
|
|
|
cache strategy.
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
Handler layer is likely never changed. Repository layer is rarely changed.
|
|
|
|
Service layer is changed daily, this is where I put so much time on.
|
|
|
|
</p>
|
|
|
|
<p>The previous question can be asked in many ways:</p>
|
|
|
|
<ul>
|
|
|
|
<li>How to move fast without breaking things?</li>
|
|
|
|
<li>How to quickly experiment new code without affecting old code?</li>
|
|
|
|
<li>...</li>
|
|
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
My answer is, as Message Broker introduce concept decoupling, loosely
|
|
|
|
coupled coding. Which means, 2 functions which do not share same business
|
|
|
|
can be deleted without breaking the other.
|
|
|
|
</p>
|
|
|
|
<p>
|
2024-01-24 03:32:12 +00:00
|
|
|
For example, we can send noti to users using SMS, Zalo, or noti-in-app (3
|
2024-01-23 19:48:42 +00:00
|
|
|
providers). They are all independently feature which serves same purpose:
|
|
|
|
alert user about something. What happen if we add providers or remove
|
|
|
|
some? Existing providers keep working as usual, new providers should
|
|
|
|
behave properly too.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
So we have send noti abstraction, which can be implement by each provider,
|
|
|
|
treat like a module (think like lego) which can be plug and play right
|
|
|
|
away.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
And when we do not need send noti anymore, we can delete whole of it which
|
|
|
|
includes all providers and still not affecting main flow.
|
|
|
|
</p>
|
2024-01-24 03:32:12 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
2024-01-24 04:38:03 +00:00
|
|
|
id="user-content-write-code-that-is-easy-to-test"
|
2024-01-24 03:32:12 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-24 04:38:03 +00:00
|
|
|
href="#write-code-that-is-easy-to-test"
|
2024-01-24 03:32:12 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-24 04:38:03 +00:00
|
|
|
>Write code that is easy to test
|
2024-01-24 03:32:12 +00:00
|
|
|
</h3>
|
|
|
|
<p>
|
|
|
|
Test is not a way to find bug, test is a way for us to make sure what we
|
2024-01-24 04:38:03 +00:00
|
|
|
code is actually what we think/expect.
|
2024-01-24 03:32:12 +00:00
|
|
|
</p>
|
|
|
|
<p>
|
2024-01-24 05:51:38 +00:00
|
|
|
Best case is test with real dependencies (real servives, real Redis, real
|
|
|
|
MySQL, real Kafka, ...). But it's not easy way to setup yourself.
|
2024-01-24 03:32:12 +00:00
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
The easier way is to use mocks. Mock all dependencies to test all possible
|
|
|
|
edge cases you can think of.
|
|
|
|
</p>
|
2024-01-24 04:38:03 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Unit tests is the standard (ZaloPay currently requires 90% test
|
|
|
|
coverage).
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Easy to test small to medium function which have simple rules,
|
|
|
|
likely single purpose with table testing technique.
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
For big, complex function, the strategy testing goes from happy case
|
|
|
|
to each single edge case, each single if else path,... try to cover
|
|
|
|
as much as you can.
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>
|
2024-01-24 05:51:38 +00:00
|
|
|
Integration tests is to test your system as a whole package, can be in 2
|
|
|
|
ways:
|
2024-01-24 04:38:03 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
Locally, which require to run in your computer with fully set up
|
|
|
|
dependencies, is hard to set up.
|
|
|
|
</li>
|
2024-01-24 05:51:38 +00:00
|
|
|
<li>
|
|
|
|
Remotely, use DEV/... env to test full business flow with all
|
|
|
|
possible scenario.
|
|
|
|
</li>
|
2024-01-24 04:38:03 +00:00
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-24 03:32:12 +00:00
|
|
|
<p>TODO: Show example</p>
|
2024-01-24 05:51:38 +00:00
|
|
|
<p>How to make code easier to test. Same idea loosely coupled as above.</p>
|
|
|
|
<p>Some tips:</p>
|
|
|
|
<ul>
|
|
|
|
<li>Rely on abstraction/interface to mock</li>
|
|
|
|
<li>
|
|
|
|
Limit relying on variable outside input (global variable, environment
|
|
|
|
variable, ...)
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
If deleting/adding code but tests are still passed, then maybe your
|
|
|
|
tests are wrong/not enough (tests is missing some code path).
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-23 19:48:42 +00:00
|
|
|
<h3>
|
|
|
|
<a
|
2024-01-23 19:53:59 +00:00
|
|
|
id="user-content-references-1"
|
2024-01-23 19:48:42 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-23 19:53:59 +00:00
|
|
|
href="#references-1"
|
2024-01-23 19:48:42 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>References
|
|
|
|
</h3>
|
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to"
|
|
|
|
rel="nofollow"
|
|
|
|
>Write code that is easy to delete, not easy to extend.</a
|
|
|
|
>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://cerebralab.com/Imaginary_Problems_Are_the_Root_of_Bad_Software"
|
|
|
|
rel="nofollow"
|
|
|
|
>Imaginary Problems Are the Root of Bad Software</a
|
|
|
|
>
|
|
|
|
</li>
|
2024-01-24 03:32:12 +00:00
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://haunt98.github.io/posts-go/2022-12-25-go-test-asap.html"
|
|
|
|
rel="nofollow"
|
|
|
|
>Speed up writing Go test ASAP</a
|
|
|
|
>
|
|
|
|
</li>
|
2024-01-23 19:48:42 +00:00
|
|
|
</ul>
|
2024-01-22 02:40:27 +00:00
|
|
|
<h2>
|
|
|
|
<a
|
2024-01-24 05:51:38 +00:00
|
|
|
id="user-content-system-design"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#system-design"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>System Design
|
|
|
|
</h2>
|
|
|
|
<h2>
|
|
|
|
<a
|
|
|
|
id="user-content-performance"
|
2024-01-22 02:40:27 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-24 05:51:38 +00:00
|
|
|
href="#performance"
|
2024-01-22 02:40:27 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-24 05:51:38 +00:00
|
|
|
>Performance
|
2024-01-22 02:40:27 +00:00
|
|
|
</h2>
|
|
|
|
<h2>
|
|
|
|
<a
|
2024-01-24 05:51:38 +00:00
|
|
|
id="user-content-security"
|
2024-01-22 02:40:27 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-24 05:51:38 +00:00
|
|
|
href="#security"
|
2024-01-22 02:40:27 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-24 05:51:38 +00:00
|
|
|
>Security
|
2024-01-22 02:40:27 +00:00
|
|
|
</h2>
|
|
|
|
<h2>
|
|
|
|
<a
|
2024-01-23 17:56:31 +00:00
|
|
|
id="user-content-damage-control"
|
2024-01-22 02:40:27 +00:00
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
2024-01-23 17:56:31 +00:00
|
|
|
href="#damage-control"
|
2024-01-22 02:40:27 +00:00
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
2024-01-23 17:56:31 +00:00
|
|
|
>Damage control
|
2024-01-22 02:40:27 +00:00
|
|
|
</h2>
|
|
|
|
<p>TODO: Take care incident</p>
|
2024-01-21 18:41:21 +00:00
|
|
|
<h2>
|
|
|
|
<a
|
|
|
|
id="user-content-bonus"
|
|
|
|
class="anchor"
|
|
|
|
aria-hidden="true"
|
|
|
|
tabindex="-1"
|
|
|
|
href="#bonus"
|
|
|
|
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
|
|
|
>Bonus
|
|
|
|
</h2>
|
2024-01-23 20:00:51 +00:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<a href="https://www.jetbrains.com/idea/download/" rel="nofollow"
|
|
|
|
>IntelliJ IDEA Ultimate</a
|
|
|
|
>: Java coding
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://www.jetbrains.com/go/download/" rel="nofollow"
|
|
|
|
>GoLand</a
|
|
|
|
>: Go coding
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://www.jetbrains.com/datagrip/download/" rel="nofollow"
|
|
|
|
>DataGrip</a
|
|
|
|
>: Database GUI
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="https://redis.com/redis-enterprise/redis-insight/"
|
|
|
|
rel="nofollow"
|
|
|
|
>RedisInsight</a
|
|
|
|
>: Redis GUI
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://orbstack.dev/download" rel="nofollow">OrbStack</a>:
|
|
|
|
Better Docker Desktop
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a href="https://kreya.app/" rel="nofollow">Kreya</a>: GRPC caller
|
|
|
|
</li>
|
|
|
|
</ul>
|
2024-01-21 11:48:28 +00:00
|
|
|
|
|
|
|
<div>
|
|
|
|
Feel free to ask me via
|
|
|
|
<a href="mailto:hauvipapro+posts@gmail.com">email</a> or
|
|
|
|
<a rel="me" href="https://hachyderm.io/@haunguyen">Mastodon</a>.
|
|
|
|
<br />Source code is available on
|
|
|
|
<a href="https://github.com/haunt98/posts-go">GitHub</a>
|
|
|
|
<a href="https://codeberg.org/yoshie/posts-go">Codeberg</a>
|
|
|
|
<a href="https://git.sr.ht/~youngyoshie/posts-go">sourcehut</a>
|
|
|
|
<a href="https://gitea.treehouse.systems/yoshie/posts-go">Treehouse</a>
|
|
|
|
<a href="https://gitlab.com/youngyoshie/posts-go">GitLab</a>
|
|
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|