A company is run by people, with different opinions, when new people get in charge, the company changes as well. Microsoft is not hostile towards Linux and OSS and embraced it, deal with it.
2,632 karma · joined June 19, 2019
- https://link-society.com
- https://krouter.cloud
- https://flowg.cloud
- https://linkdd.github.io
- https://david-delassus.medium.comA company is run by people, with different opinions, when new people get in charge, the company changes as well. Microsoft is not hostile towards Linux and OSS and embraced it, deal with it.
Though, Gitlab CI is less powerful than Github Actions. With Gitlab CI, you can only have one pipeline per repository, and you have to play with conditional includes based on some CI variable to implement multiple pipelines.
While with Github Actions, you can simply have multiple workflows, listening to different kind of events (webhook, issues/pull requests events, ...)
In the end, only for-profit businesses remain in the race, Gitlab, Github, Bitbucket, ...
A business will do everything to stay profitable. That is the purpose of a business.
Any alternative to Reddit that gain some popularity is doomed to meet the same problems of scalability, infrastructure costs, and content moderation. Those things are not free, they cost money, therefore will be run by a business, doing business, trying to stay profitable, and will become "user hostile" once it is not profitable anymore.
This is the fate of any successful centralized social network.
Are PyPI, cargo, Maven, etc... all immune to supply chain attacks?
Genuine question: How is PyPI different from NPM, Maven, crates.io, etc..?
Why "Python packaging is a mess" and not the others?
In Python type hints are just that: hints.
mypy is a very poor static analyzer, and a lot of Python code (especially in frameworks like Django) is inherently dynamic and cannot be type-checked. And that's ok.
Though, documenting your API with type-hints really helps, especially with VSCode (via pyright) popovers when you hover a symbol/function.
class Recurse(Exception):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
class Terminate(Exception):
def __init__(self, retval):
self.retval = retval
def tailrec(func):
def wrapper(*args, **kwargs):
while True:
try:
func(*args, **kwargs)
except Recurse as r:
args = r.args
kwargs = r.kwargs
except Terminate as t:
return t.retval
return wrapper
@tailrec
def fact(n, acc=1):
if n == 0:
raise Terminate(acc)
else:
raise Recurse(n - 1, acc * n)
Of course, it will be slow because it relies on exceptions :PIt is obviously a toy experiment and it took my 5s to realize that.
While it does not say "this is a lisp", it also does not say "this is a new shiny thing that will change the world". In fact, it says nothing except "look at what i did".
Replace chainsaw with:
- knife
- guns
- C programming language
There are tools and methods (and a lot of discipline) to ensure safety in C: - compiler's sanitizers[0]:
- address: to detect out-of-bounds and use-after-free bugs
- pointer-compare, pointer-subtract: to detect invalid operation when pointers are non null
- shadow-call-stack: to detect return address overwrites (stack buffer overflows)
- thread: to detect data races
- leak: to detect memory leaks
- undefined: to detect undefined behaviors
- ...
- static analysis tools, like Splint[1]
Would you ride a motorbike without the proper protections (helmet, heavy jacket, ...) ?[0] - https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.h...
Also, a disciplined C programmer will always keep the size of the buffer near the buffer itself.
For example, using something like this is perfectly safe (though, we can see the performance hit of those if-statements if used intensively):
struct buffer {
size_t size;
char *data;
};
void buffer_alloc(struct buffer *buf, size_t size) {
if (buf != NULL) {
buf->data = calloc(size, sizeof(char));
if (buf->data != NULL) {
buf->size = size;
}
}
}
void buffer_free(struct buffer *buf) {
if (buf != NULL && buf->data != NULL) {
free(buf->data);
buf->size = 0;
}
}
bool buffer_read(struct buffer buf, size_t offset, char *dest) {
if (dest != NULL) {
if (buf.data != NULL && offset >= 0 && offset < buf.size) {
*dest = buf.data[offset];
return true;
}
}
return false;
}
int main() {
struct buffer buf = {0};
buffer_alloc(&buf, 123);
char c;
buffer_read(buf, 124, &c);
buffer_free(&buf);
return 0;
}
It is possible to write safe C code, but it takes understanding of the language, sometimes of the compiler as well.The whole "There is no programmer who don't make mistakes" argument is fallacious at best.
C is like a chainsaw, there are certain rules on how to use it safely. Yes you can cut limbs with a chainsaw, that does not make the chainsaw useless.
---
EDIT: For more details, OCI (Open Container Initiative) defines a few things:
- an image format (how to create them)
- a runtime specification (how to run them)
- ...
A container runtime is an implementation of one of those things.Docker is a set of tools hidden under a unified CLI which gives you the ability to:
- create images
- upload/download images to/from a registry
- run images with volumes, networking configuration, environment variables, ...
But Docker is only one host.Kubernetes is an interface to abstract a cluster of hosts. Everything is described as a "resource" which goes through the "control loop":
1. the user uses the k8s REST API to create/read/update/delete the resource
2. the k8s api server will contact admission controllers (via webhook) to authorize (and/or mutate) the action
3. the action is persisted to a distributed database (usually, etcd)
4. then, controllers are notified of the change and will run the side effects
This is the simplified version. But what is stored in the distributed database is called the "desired" state, and controllers have the duty of observing the real state (the "observed" state) and make it converge towards the "desired" state.So a "Pod" controller's job will be to observe Docker instances, to check what containers are running, and start/stop the containers based on what "Pod" resources exists in k8s's database.
A "Deployment" controller's job will be to observe the "Pod" resources in the k8s database and create/update/delete them based on what "Deployment" resources exists in k8s's database.
etc...
In theory, Kubernetes does not need docker. You could have a "proxmox" controller which would start/stop virtual machines instead.
Kubernetes provides a lot of tooling for storage management, secret management, networking, workload management, etc... so that you can manage it all with a unified REST API.
The very nature of the "control loop" makes it very extensible, allowing you to build layers of abstraction on top of layers of abstraction on top of layers of abstraction ... A real "onion cloud" if I dare say it.
To be fair, my needs aren't that complex, I do mostly CRUD on database tables, most of the business logic is handled in my code, eventually wrapped in a `@transaction.atomic` (thank you Django).
If I need stored procedures, I'll add a Django migration which creates the said procedure with raw SQL.
If I need to manage the schema myself, I'll also make a Django migration with raw SQL, then I'll create Django unmanaged models.
For the portability across RDBMS, it's nice to have an SQLite database in the dev environment, so that the developer does not need to run a PostgreSQL instance (in docker or whatever). And for the test suite, I even use an in-memory SQLite database.
I do put my ORM queries in a specific module which provides a higher level API, I will have functions like `get_users_sent_invites` or `publish_article` etc... so that the rest of my code never sees database code. A function `get_user_by_id` will return the User model or None, and handle the ORM's potential exceptions to return meaningful errors to the business logic.
It also makes database access easier to test and benchmark. You might call this DAO or not, terms are irrelevant, it's just good practice to separate concerns IMHO.
https://en.wikipedia.org/wiki/Compiler
> In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language).
> The name "compiler" is primarily used for programs that translate source code from a high-level programming language to a low-level programming language (e.g. assembly language, object code, or machine code) to create an executable program.
> a program that translates between high-level languages, usually called a source-to-source compiler or transpile
Since when is C a high-level language?
There is no need to be so pedantic. A transpiler is a special kind of compiler.
This is why some JIT'd languages (like Javascript) can be sometimes faster than C.
Almost correct :D
The BDD scenarii also act as "user stories" in a Scrum/Agile workflow. It was a nice way to re-use what the product owner requested.
I'll look more closely into it then :)
> Gherkin is awful at that.
When I use BDD, it's because I'm handed down scenarii written by non-tech people. And it's always in the Gherkin format, then I chose the right Gherkin engine for the right tech stack. That might be cucumber, or another.
Unfortunately, I have to say it... I hate YAML with a passion, k8s, github actions/gitlab ci, ansible, etc... When I'm doing ops jobs, I feel like I'm coding in YAML.
Btw, this hate for YAML birthed https://linkdd.github.io/tricorder/ :P
Understood, no need to argue then, I don't think any amount of proof would change your mind...
Django-Ninja use them to do dependency injection and generate schemas for your API (using pydantic). autodoc use them to generate the documentation of your function. etc...
It does not replace duck typing, proof here: https://peps.python.org/pep-0544/
But if you try to invent your own language (like Tolkien did), you are not "expressing an idea" (aka: speaking), you are creating a framework to express ideas.
When you write a program, you follow a grammar to express an idea (the solution to the problem you are tackling). When you design a programming language, you are creating that grammar (using another language to implement it afterwards).
Also, here "programming language designers" designates "people who are working on their own programming language". Those are the people I'm talking to when writing this Rust crate and blog article. The webdev doing CRUD microservices could not care less about astmaker.
For "member-only" articles, you get a remuneration proportional to the "member reading time". For members who subscribed via your "onboarding" page (I don't have one I think, I don't know, I should look into it), you get a remuneration proportional to their reading time on any articles, not just yours.
The friend-tag on an article's link removes the paywall, allowing you to increase views when sharing the article. The more views your article has, the more Medium will promote it. But many platforms removes it as it may have trackers associated to it (I haven't looked into that to confirm or infirm).
EDIT: Fun fact, the Javascript subreddit even forbids medium links because they consider the content to be spammy 99% of the time.
For Medium, they want to incite people to buy a subscription, hence the paywall. But they also want writers to generate the content for them, hence the friend-link and remuneration model.
The remuneration is quite small, so I don't think they are loosing money. But short clickbaity articles about how console.log works can generate a LOT of views, and writers spamming those can earn up to 300$/month (or maybe more), hence the bad reputation of Medium. I'm not interested in doing that, I mainly write to share about my work and just enjoy the small remuneration as a reward for my open-source work: it's not entirely useless if I can get a meal per month out of it ^^
EDIT: Out of transparency, by writing an average of 1-2 articles/month on topics I enjoy, I managed to make an average of 10-15$/month. The ones about Rust are the more profitable ones, go figure :P
Agreed, though the point of the article was to explore the Rust ecosystem. When learning, a little bit of blasphemy isn't a bad thing :)
Not only the type system is quite nice, the ecosystem is full of nice crates:
- lexing: logos
- parsers: rust-peg / lalrpop / nom
- utilities: string-interner / linecol / snailquote
I also wrote about this here: https://david-delassus.medium.com/writing-a-simple-lisp-inte... (friend link, no paywall)