The trickiness of HTML checkbox

I'm using Lustre framework to rebuild the Admin area of this blog. When implementing the form for editing blog post, I'm surprised how tricky to handle the checkbox, which may look simple at first.

In CRUD applications, people often use the checkbox to represent a boolean field. The "checked" status is for True and unchecked is for False. Take this form as example, when I want to publish a post, I tick the "Published" checkbox and save. If I want to unpublish, I untick and save.

Checkbox as boolean field

Most of people don't see any issue with this usage. When I make frontend apps with VueJS, with an edit form like this, I often bind each <input> element with a reactive variable, via v-model:

...

Điểm mới của Python 3.14: Chế độ free-threading

Một điểm mới khác, khá quan trọng, của Python 3.14, nhưng không "đập vào mắt người dùng" là chế độ "free-threading". Đây là chế độ mà CPython tắt "Global Interpreter Lock" (GIL), một loại khóa trước đây dùng để ngăn chặn nhiều thread cùng truy cập, sửa đổi dữ liệu thuộc kiểu riêng của CPython. Nay với chế độ "free-threading" thì các thread được chạy song song thoải mái hơn (tốc độ xử lý của ứng dụng đa luồng cũng tăng lên).

Nếu bạn viết code Python, không cần phải quan tâm vì ở mức độ này không nhìn thấy GIL. Chỉ khi bạn viết extension cho Python bằng ngôn ngữ biên dịch (C, C++, Rust) thì mới phải động vào GIL. Khi CPython gỡ bỏ GIL, tương tự như ngã tư bỏ đi cảnh sát đứng phân luồng, thì tác giả các extension này phải tự đảm bảo code mình là thread-safe, tự áp dụng các phương tiện, chiêu thức khác để tránh code mình bị crash, deadlock trong môi trường đa luồng. Điều này lại là cơ hội tỏa sáng cho các extension viết bằng Rust. Một trong các điểm "ăn tiền" của Rust là "fearless concurrency". Rust có các luật kiểm tra ownership, lifetime chặt chẽ, có các phương tiện dành cho lập trình đa luồng giúp bạn tránh tối đa các lỗi hay gặp, khó mò trong lập trình đa luồng.

Free-threading

Mình có một extension, viết bằng Rust, để làm cho nó tương thích với "free-threading" thì cực kỳ dễ, chỉ cần khai báo gil_used = false, vì vốn từ đầu nó đã chạy mà không cần "xin" GIL rồi.

...

Lustre / Gleam: How to create modal popup

Modal dialog is a often used UI element, but in web tech, developers used to spend quite much effort to implement it, before the introduction of CSS frameworks like Bootstrap. It was tricky to position the dialog in the center of the window, to blur the content behind it. It was tricky to let user close the popup by clicking outside it.

It is until the HTML standard introduces the native <dialog> element. Now developers can create a modal popup with ease, except if their users are using Safari, which often lags behind the modern web features. Put Safari aside, now we see how to create the native modal popup in Lustre framework (Gleam language).

My use case is the post compose form of this blog. The original content is in Markdown. The form has a "Preview" button so that if I click it, a modal popup appears and loads the rendered HTML of that Markdown code.

Form

...

Khi một ngôn ngữ lập trình không có lệnh `return`

Từ khóa return quá quen thuộc khi nó có mặt trong hầu như mọi ngôn ngữ lập trình, không chỉ được dùng ở cuối hàm như một lẽ thường, nó còn được dùng ở giữa hàm khi cần dừng chạy hàm sớm khi gặp điều kiện nào đó, ví dụ:

async def process_mqtt_message(
    topic: str, payload: bytes, pg_session: AsyncSession, red: Redis[str]
) -> ActuatorProcessResult | NodeProcessResult | None:
    if m := REGEX_TOPIC_SN_ACTUATOR_STATUS.match(topic):
...

Lustre / Gleam: How to open confirm dialog

Given that we have a list of items, and a "delete" button for each item. We want that when user clicks the button, a confirm dialog will appear, asking user one more time, before proceeding with API call to delete the item from the database.

Delete confirmation

Here is how to do that in Lustre (a frontend web framework in Gleam language). The dialog in this example is the HTML native dialog created by window.confirm() method, in case we want some thing quick and simple. If you want to use <dialog> element, I will write later.

First, let's write a view function to prepare the HTML:

...

structlog-journald, ghi log vào journald

Hôm nay mình phát hành thư viện structlog-journald v0.5.0. Thư viện này tận dụng lợi thế của journald để giúp debug hệ thống multi-tenant dễ dàng hơn. Đây là những hệ thống phục vụ nhiều khách hàng, với cách làm thông thường thì log của nhiều khách hàng sẽ trộn lẫn với nhau gây khó khăn cho việc debug. Thư viện này cho phép gắn thông tin phụ (như ID khách hàng) khi ghi log rồi sau đó khi xem log bằng journalctl có thể lọc theo thông tin phụ đó. Bằng cách đó ta có thể tập trung vào một đối tượng cụ thể để truy vết.

Minh họa:

demo

...

Sinh viên đang khiến mình thụt lùi với AI

Để nâng cao năng suất làm việc của lập trình viên thì có nhiều phương án, phương án dùng AI sinh code chỉ là một, nhưng là phương án được marketing ồn ào nhất. Cũng phải thôi, để huấn luyện các mô hình AI đó thì tốn rất nhiều tiền nên các cty chủ quản phải marketing rầm rộ, dùng tới cả chiêu bài hù dọa để cốt bán được hàng.

Vấn đề là những bài marketing làm lệch lạc phương hướng của các sinh viên rất nhiều. Thay vì tự rèn luyện về thuật toán, nghệ thuật của các ngôn ngữ, các good practices, các bí ẩn sâu xa của công nghệ để nâng trình độ của mình vượt lên AI, để có thể tự tin review và bác bỏ code do AI sinh ra, thì giờ nhiều bạn SV lại thấy tự hào vì "AI làm hết cho mình", tự hào vì dành một phần lớn trong số tiền "trợ cấp" hàng tháng để "cúng" cho AI.

Mùa Google Summer of Code năm nay, mình đọc một số proposal của các SV đăng ký dự thi, và gần 100% có giọng văn, cách trình bày của AI, nhưng phần lớn trong số đó bị loại vì lạc đề và dựa vào kiến thức cũ kĩ ko còn đúng với thực tế. Ví dụ có một đề bài là nâng cấp một bộ code cũ của dự án nọ từ Vue 2 lên Vue 3. Sinh viên ấy viết proposal để đề xuất dùng Options API thay vì Compositon API, với một trong những lý do là nếu dùng Composition API thì phải dùng công cụ Volar. Cái không thực tế ở đây là ngoài Volar thì làm gì còn công cụ LSP server cho Vue nào tốt hơn nó nữa. Các sinh viên đó thừa nhận là dùng AI để làm nhưng coi những lời của AI như thần, ko bao giờ đặt nghi vấn, vặn vẹo lại.

Vừa nãy có nói, dùng AI tạo sinh chỉ là một phương án. Phương án còn lại là gì? Là tập dùng các công cụ dòng lệnh, và các đồ chơi trên HĐH Linux (vế này ko cần nghe nếu nghề của bạn là lập trình ứng dụng Windows, iOS, MacOS). Trong phần lớn các trường hợp có dùng AI để phụ viết code thì mình suy nghĩ ra giải pháp nhanh hơn AI (và nhìn AI "thinking..." mà sốt cả ruột), nhưng mình lại gõ phím rất chậm, nên sẽ làm chậm hơn AI nếu code đó phải viết ở nhiều nơi nhiều chỗ. Tuy nhiên có nhiều trường hợp, sử dụng thành thạo các công cụ dòng lệnh thì mình đỡ phải viết code nhiều (cứ phím tắt, autocomplete mà nện) thì cuối cùng lại làm nhanh hơn AI (nếu tính cả thời gian viết prompt nhiều lần để lái AI cho làm đúng ý mình).

...

Set a terminal emulator to be default for Ubuntu 25.04+

Ubuntu comes with a default terminal emulator, GNOME Terminal. But due to the limit of the underlying vte, many users don't want to use it. If you want to set a different program as default terminal, so that you can invoke it with Ctrl + Alt + T, here is how to do it.

Determine the desktop file of your terminal, by searching in /usr/share/applications/ or ~/.local/share/applications/. For example, kitty has one at ~/.local/share/applications/kitty.desktop, Ghostty has one at /usr/share/applications/com.mitchellh.ghostty.desktop.

Edit the ~/.config/xdg-terminals.list file (create it if it does not exist). Add the name of the desktop file at the top. For example, here is mine:

...


Make Nginx Unit controllable from non-root user

Recently I heard about Nginx Unit, a piece of software which lets you run Python web application on production with less components. It's like you let Nginx run your Python code, no longer Nginx - Gunicorn separation. So I installed it and learned how to use, but the default setup is not convenient: You have to switch to root user too often. So I share extra steps of setup to save you from it.

Nginx Unit is not configured via file like Nginx. It is done via HTTP API, which is nice because Nginx Unit can get update with new configuration without restarting, although I don't know what is the benefit over using SIGHUP signal to trigger rereading. The API communication is done via Unix domain socket, leveraging Linux authentication for restricting access, which is very reasonable because we don't have to remember port, username and password. But the issue is that, the default setup makes the socket writable by root only.

Unit control socket

The annoyance come from the combination of these factors:

...