MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

PostgreSQL(Alternations)

2026-03-20 17:12:09

-ALTER TABLE → modify existing table structure.
-Operations: add/drop column, change datatype, rename column/table, add/drop constraints.

Common ALTER TABLE Operations

1. Add a new column

ALTER TABLE Employee
ADD COLUMN email VARCHAR(100);

2. Drop (remove) a column

ALTER TABLE Employee
DROP COLUMN email;

3. Change datatype of a column


ALTER TABLE Employee
ALTER COLUMN salary TYPE NUMERIC(12,2);

4. Rename a column

ALTER TABLE Employee
RENAME COLUMN name TO full_name;

5. Add a constraint

ALTER TABLE Employee
ADD CONSTRAINT salary_positive CHECK (salary > 0);

6. Drop a constraint

ALTER TABLE Employee
DROP CONSTRAINT salary_positive;

7. Rename the table itself

ALTER TABLE Employee
RENAME TO Staff;

This is the basic PostgreSQL...

See you soon in the next blog!....

How to Print Alternate Elements of an Array in JavaScript (DSA)

2026-03-20 17:11:10

🧩 Problem: Alternate Elements of an Array

Given an array arr[], your task is to print every alternate element of the array starting from the first element.

In other words, print elements at index 0, 2, 4, ... and skip the rest.

📥 Input

An array of integers arr[]

📤 Output

Print the alternate elements of the array (starting from index 0)

🧪 Examples

Example 1:

Input: arr[] = [10, 20, 30, 40, 50]
Output: 10 30 50

Explanation:
Print the first element (10), skip the second (20), print the third (30), skip the fourth (40), and print the fifth (50).

Example 2:

Input: arr[] = [-5, 1, 4, 2, 12]
Output: -5 4 12

⚠️ Constraints

  • 1 ≤ arr.length ≤ 10^5
  • -10^9 ≤ arr[i] ≤ 10^9

Solution

Approch 1

Google Consent Mode Explained (React + TypeScript)

2026-03-20 17:10:01

Privacy-First Analytics for Modern Web Apps (Part 2)

To address the data-loss problem (see Part 1), Google introduced Consent Mode.

The goal is to allow analytics tags to load before consent, but with restricted behaviour.

What Advanced Consent Mode does

With Advanced Consent Mode:

User visits site
  ↓
Google tag loads
  ↓
Consent state = denied
  ↓
Cookieless pings sent

These pings may contain:

  • page URL
  • timestamp
  • browser / device type
  • approximate region

But they do not set cookies.

Google uses these signals to estimate user behaviour.

Basic vs Advanced Consent Mode

Basic consent mode: Google Analytics loads only after consent

Advanced consent mode: Google tag loads immediately with restricted tracking

Implementing Basic Consent Mode (React + TypeScript)

Example cookie consent logic:

import ReactGA from "react-ga4"

export function enableAnalytics() {
  const GA_ID = process.env.REACT_APP_GA_ID

  if (GA_ID) {
    ReactGA.initialize(GA_ID)
  }
}

Called when the user clicks Accept.

Implementing Advanced Consent Mode

Advanced Consent Mode uses the gtag consent API.

Default state:

window.gtag("consent", "default", {
  analytics_storage: "denied",
  ad_storage: "denied"
})

When the user accepts:

window.gtag("consent", "update", {
  analytics_storage: "granted",
  ad_storage: "granted"
})

When the user declines:

window.gtag("consent", "update", {
  analytics_storage: "denied"
})

This allows the Google tag to operate in restricted cookieless mode.

Series: Privacy-First Analytics for Modern Web Apps

  1. Why Cookie Consent Breaks Your Analytics
  2. Google Consent Mode Explained (React + TypeScript)
  3. Ads, Tracking, and the Legal Reality in the EU and UK
  4. The Hybrid Analytics Architecture

About this series

This series is based on real-world work building CSSEXY, a visual UI platform where understanding user behaviour is essential for improving the product.

All articles are also available on CSSEXY and there in the Gallery.

Tích hợp Hot Update vào dự án để tối ưu thời gian cho internal testing

2026-03-20 17:01:49

Giới thiệu

const x = "dừng tay, chuyển nhánh, pull code, build app"

Nếu anh em đã từng làm React Native một thời gian, chắc chắn không ít lần nghe câu quen thuộc từ QA: "Em ơi build lại cho chị cái task này với." Rồi phải x đợi 20–30 phút, gửi file, rồi lại ngồi chờ phản hồi. Cứ lặp đi lặp lại như vậy mỗi ngày.

Vấn đề x có thể giải quyết bằng việc tích hợp CI/CD để tự động hóa quá trình này. Nhưng vẫn không tránh khỏi việc mất thời gian, QA vẫn phải đợi pipeline build xong mới có app để test. Chưa kể có các task cần khách UAT trên TestFlight thì vẫn không tránh khỏi việc dev cần tự mình build lại app để đẩy lên cho khách UAT.

Đây không phải vấn đề của một người — đây là vấn đề của cả team. Và nó hoàn toàn có thể giải quyết được.

Tài liệu này mô tả hệ thống Dynamic Hot Update Channel — một giải pháp OTA (Over-The-Air) update được thiết lập trong project, cho phép dev deploy thay đổi JavaScript trực tiếp lên thiết bị của QA trong vài giây, không cần build lại native app, không cần qua TestFlight, không cần chờ đợi.

1. Vấn Đề Cần Giải Quyết

Trước khi đi vào giải pháp, hãy nhìn thẳng vào những điểm bất cập hiện tại:

🐢 QA Cycle quá chậm

Mỗi lần có thay đổi JavaScript — dù chỉ là sửa một dòng text hay điều chỉnh màu button — team vẫn phải trải qua toàn bộ quy trình: CI/CD build ra file .ipa hoặc .apk, đợi 15–20 phút, rồi QA mới có app để test -> Lãng phí thời gian không cần thiết.

🐢 🐢 Khách UAT các task riêng lẻ

Flow CI/CD hiện tại chỉ support internal test cho case app IOS. Nên việc khách UAT task thì vẫn cần dev phải tự build app rồi đẩy lên TestFlight để khách có thể test. Case này là gây mất thời gian nhất vì dev phải thực hiện đúng một vòng x để có app cho khách UAT.

2. Giải Pháp: OTA Update + Switch Channel

OTA Update là gì?

Hãy tưởng tượng một ứng dụng React Native như một rạp chiếu phim. Phần native (iOS/Android shell) chính là toàn bộ cơ sở hạ tầng của rạp — tòa nhà, màn hình, hệ thống âm thanh, ghế ngồi. Phần JavaScript bundle chính là bộ phim đang chiếu - nội dung thực sự mà khán giả đến để xem.

Khi muốn chiếu phim mới, bạn không cần phá rạp đi xây lại. Bạn chỉ cần thay cuộn phim. Đó chính xác là cách OTA update hoạt động: thay vì build lại toàn bộ app, chúng ta chỉ đẩy một JavaScript bundle mới lên server. App tự kiểm tra, tải về và chạy — người dùng thấy version mới mà không cần làm gì cả.

Nhưng nếu muốn nâng cấp hệ thống âm thanh Dolby hay lắp thêm máy chiếu 4K — lúc đó mới cần đụng đến phần xây dựng vật lý. Tương tự, khi app cần thêm native module, thay đổi cấu hình iOS/Android, hay cập nhật dependency có native code — bạn vẫn phải build lại native app như bình thường.

Giới hạn quan trọng: OTA update chỉ áp dụng được cho thay đổi ở tầng JavaScript và Assets (hình ảnh, font). Nếu thêm native module mới, thay đổi cấu hình iOS/Android, hay cập nhật dependency có native code — vẫn phải build lại native app như bình thường.

Switch Channel là gì?

Thay vì có một staging environment duy nhất cho tất cả, mình sẽ tạo ra các channel riêng biệt — mỗi channel là một "làn đường" độc lập chứa JavaScript bundle của một task cụ thể.

Ví dụ thực tế:

  • Channel stg → chứa code của nhánh develop- nhánh ổn định nhất và sẽ là nhánh được chọn để build Native App.
  • Channel V567APP-199 → chứa code JS của task V567APP-199 rẽ từ develop, QA muốn test task này thì chỉ cần switch sang "làn đường" - Channel V567APP-199 là có code mới nhất của task để test.

QA có thể switch qua lại giữa các channel ngay trên một app đã cài sẵn, không cần cài lại, không cần bản build mới.

3. Kiến Trúc & Infrastructure

Hệ thống được xây dựng trên hot-updater với Cloudflare làm backend, được chọn vì chi phí thấp, tốc độ cao phù hợp với mục đích phục vụ cho internal test hiện tại.

┌─────────────────────────────────────────────────────────┐
│                    Cloudflare Infrastructure            │
│                                                         │
│  ┌──────────────────┐      ┌──────────────────────────┐ │
│  │  Cloudflare R2   │      │     Cloudflare D1        │ │
│  │  (Object Storage)│      │     (SQLite Database)    │ │
│  │                  │      │                          │ │
│  │  • bundle.js     │      │  channel | version | ... │ │
│  │  • assets/       │      │  stg     | 4.0.0   | ... │ │
│  │  • fonts/        │      │  V567-99 | 4.0.0   | ... │ │
│  └──────────────────┘      └──────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
         ▲                              ▲
         │ deploy                       │ query metadata
         │                              │
┌────────┴──────────┐        ┌──────────┴──────────────┐
│   Developer CLI   │        │    React Native App     │
│  yarn hot:task:   │        │  (on QA's device)       │
│  ios V567APP-199  │        │  → checks channel,      │
└───────────────────┘        │  → downloads bundle     │
                             │  → reloads JS engine    │
                             └─────────────────────────┘

Các thành phần chính:

Thành phần Vai trò
Cloudflare R2 Lưu trữ JavaScript bundle và static assets
Cloudflare D1 Database theo dõi metadata: channel nào đang chạy bundle nào, version nào
hot-updater CLI Tool deploy bundle từ máy developer lên Cloudflare
DevChannelSwitcher UI trong app để QA switch channel

Các channel được định nghĩa:

  • stg — Channel mặc định, luôn phản ánh code của nhánh staging chính (develop). Logic hiện tại khi build native app với config staging sẽ auto set default channel là stg
  • [Task-Name] — Ephemeral channel (tồn tại tạm thời) cho từng Jira ticket, ví dụ V567APP-199

4. Tích hợp

A. Infrastructure Configuration

Việc set-up, cấu hình anh em có thể check trực tiếp trên docs của Hot Updater nhé 🫡: https://hot-updater.dev/docs/get-started/introduction

B. Deployment Scripts

Các script được định nghĩa trong package.json, sử dụng biến môi trường từ .env.stg:

"hot:console":       "npx hot-updater console",
"hot:stg:ios":       "ENVFILE=.env.stg npx hot-updater deploy -p ios -t \"4.0.0\" --channel stg",
"hot:stg:android":   "ENVFILE=.env.stg npx hot-updater deploy -p android -t \"4.0.0\" --channel stg",
"hot:stg":           "yarn hot:stg:ios && yarn hot:stg:android",
"hot:task:ios":      "ENVFILE=.env.stg npx hot-updater deploy -p ios -t \"4.0.0\" --channel",
"hot:task:android":  "ENVFILE=.env.stg npx hot-updater deploy -p android -t \"4.0.0\" --channel"

Mổ xẻ từng phần của một lệnh deploy điển hình:

ENVFILE=.env.stg  npx hot-updater deploy  -p ios  -t "4.0.0"  --channel stg
│                 │                        │        │              │
│                 │                        │        │              └─ Tên channel sẽ nhận bundle này
│                 │                        │        └─ Version targeting: chỉ apply cho app 4.0.0
│                 │                        └─ Platform: ios hoặc android
│                 └─ Lệnh deploy của hot-updater CLI
└─ Inject .env.stg làm môi trường build (API keys, app config, v.v.)

Tại sao cần ENVFILE=.env.stg?

Khi JavaScript bundle được tạo ra, nó sẽ bake-in các giá trị từ file .env vào trong bundle — bao gồm API endpoint, feature flags, v.v. Chỉ định .env.stg đảm bảo bundle được build với đúng config của môi trường staging, không phải production.

DEVELOPMENT_MODE=true để check điều kiện chỉ implement với các bản build app ở staging không gây rủi do đến các bản build của production.

const isDevelopmentMode = Boolean(Config.DEVELOPMENT_MODE === "true")

export default isDevelopmentMode
  ? HotUpdater.wrap({
      baseURL: "[https://hot-update.staging.workers.dev/api/check-update](https://hot-update.staging.workers.dev/api/check-update)",
      updateStrategy: "appVersion",
      updateMode: "auto",
    })(App)
  : App

Tại sao cần -t "4.0.0"?

hot-updater.config.ts đang dùng updateStrategy: "appVersion" — nghĩa là Cloudflare D1 sẽ lưu thêm thông tin version targeting cho mỗi bundle. Khi app khởi động và check update, nó gửi kèm app version hiện tại. Cloudflare D1 chỉ trả về bundle nếu version của thiết bị thỏa điều kiện 4.0.0 ( Version chung của các bản build staging )

Sự khác biệt giữa hot:stghot:task:

hot:stg deploy lên channel cố định stg — channel này luôn tồn tại và là nơi toàn bộ team nhận update sau mỗi lần merge.

hot:task:ioshot:task:android không có --channel cố định — dev phải tự append tên channel khi gọi:

yarn hot:task:ios V567APP-199
# tương đương với:
# ENVFILE=.env.stg npx hot-updater deploy -p ios -t "4.0.0" --channel V567APP-199

C. In-App Dev Channel Switcher

DevChannelSwitcher.tsx — Một FAB (Floating Action Button) được inject vào root của app, chỉ hiển thị khi DEVELOPMENT_MODE=true (tức là chỉ có trên bản staging, không bao giờ xuất hiện trên production).

UI này cung cấp một modal với 3 chức năng:

  1. Hiển thị channel đang active hiện tại
  2. Input để nhập tên channel mới và switch sang bundle tương ứng
  3. Nút Reset để quay về channel stg mặc định

NOTE: Do logic của hot update, bắt buộc cần phải chuyển về default channel rồi sau đó mới có thể chuyển đến các Ephemeral channel khác. Ví dụ từ default channel stg chuyển sang channel V567APP-199 để test. Test xong, muốn sang task V567APP-200 thì cần Reset về stg sau đó chuyển sang task V567APP-200 không được chuyển trực tiếp từ V567APP-199 qua V567APP-200.

App ở default channel App ở Ephemeral channel
1. App ở default channel 2. App ở Ephemeral channel

5. Quy Trình Làm Việc Hàng Ngày

👨‍💻 Dev— Deploy một task lên channel riêng

Khi hoàn thành task V567APP-199 và muốn QA bắt đầu test:

Bước 1: Đảm bảo code đã sẵn sàng trên branch local.

Bước 2: Chạy lệnh deploy lên channel riêng của task:

yarn hot:task:ios V567APP-199
yarn hot:task:android V567APP-199

Lệnh này sẽ bundle JavaScript hiện tại ở local và push lên Cloudflare dưới channel V567APP-199. Toàn bộ quá trình mất khoảng 1–2 phút, thay vì 20 phút build native.

Bước 3: Ping QA trên Jira/Mattermost: "Task V567APP-199 Không có phần update native code và đã được deploy lên rồi"

Lưu ý: Mỗi lần chạy lại lệnh deploy cho cùng một channel, bundle cũ sẽ bị ghi đè. QA nếu đang ở channel đó rồi thì chỉ cần đợi app tải về bundle vừa được deploy lên là có code mới nhất vừa được dev update để test, không cần làm gì thêm. 😳

🕵️‍♂️ QA — Switch sang channel của task cần test

Bước 1: Mở Staging App trên thiết bị.

Bước 2: Tap vào icon 🔥 ở góc dưới bên phải màn hình để mở Task Switcher.

Bước 3: Nhập tên task vào ô input: V567APP-199

Bước 4: Tap "🚀 Switch task".

App sẽ tự động tải JavaScript bundle của task đó từ Cloudflare và reload. Sau khi reload xong, app sẽ chạy đúng code của V567APP-199.

Bước 5 (khi test xong): Mở lại Task Switcher → tap "🔄 Reset" để quay về channel stg mặc định.

NOTE: Có thể switch qua lại giữa nhiều task trong cùng một buổi test mà không cần cài lại app hay làm gì phức tạp. Chỉ cần nhập đúng tên channel và tap Switch.

GIF mô tả

🚀 Release Management — Merge vào Staging chính

Khi PR của V567APP-199 đã được approve và merge vào nhánh staging chính, cần cập nhật channel stg để toàn bộ team nhận được code mới:

Bước 1: Pull code staging mới nhất về máy.

Bước 2: Deploy lên channel stg:

yarn hot:stg

Script này sẽ deploy song song cho cả iOS và Android. Từ lần mở app tiếp theo, mọi thiết bị đang dùng channel stg sẽ tự động fetch và apply bundle mới.

Quan trọng: Chỉ chạy yarn hot:stg sau khi PR đã được merge và code trên nhánh staging đã ổn định. Tránh deploy code đang dở dang lên channel stg vì nó sẽ ảnh hưởng đến toàn bộ team.

🎛️ Dashboard — Quản lý và Rollback

Để xem toàn bộ lịch sử deployment, kiểm tra bundle nào đang active trên channel nào, hoặc thực hiện rollback khi cần:

yarn hot:console

Lệnh này mở một local web interface kết nối trực tiếp với Cloudflare D1 và R2, cho phé:

  • Xem danh sách tất cả channels và bundle đang active
  • Force rollback về một bundle cũ hơn chỉ bằng vài click

6. Tóm Tắt So Sánh

Quy trình cũ Với Hot Update Channel
Thời gian deploy cho QA 10–15 phút đợi tool (build native), 20-30 phút cho đẩy TestFlight UAT 1–2 phút
Khi update code ở task Cần build lại app Không cần làm gì, app tự tải code mới nhất về
Test song song nhiều task Cần nhiều bản build riêng Switch channel ngay trên một app
Rollback khi có bug Build lại từ commit cũ Rollback qua dashboard, tức thì
Áp dụng cho Mọi thay đổi Chỉ JS/Assets, không áp dụng cho native changes

7. Những Điều Cần Lưu Ý

  • Không dùng cho native changes: Nếu task của dev có thay đổi native code (thêm pod, sửa AndroidManifest, cập nhật native module), dev vẫn phải build native app bình thường. Hot update chỉ dành cho JS/Assets.
  • Version targeting: Bundle chỉ apply cho app 4.0.0 (default version của staging). Thiết bị đang chạy app version khác sẽ không nhận update.
  • .env.hotupdater là file nhạy cảm: Không commit file này lên git. Nếu cần onboard thành viên mới, chia sẻ qua kênh bảo mật riêng.

PostgreSql(Filtering & Sorting)

2026-03-20 17:01:03

Filtering (WHERE clause)

Filtering means → only certain rows are selected based on a condition.

SELECT name, salary
FROM Employee
WHERE salary > 55000;

Output:

name salary
Priya 60000
Meena 58000
Arun 62000

Sorting (ORDER BY clause)

Sorting means → arrange rows in ascending (ASC) or descending (DESC) order.

SELECT name, salary
FROM Employee
ORDER BY salary DESC;

See you soon in the next topic!.......