Laraue Blog — .NET, Nuxt, Telegram Bots and AI Workflow

Real code, real decisions, real tradeoffs. We write about what we build — .NET libraries, Telegram bots, AI integrations, and the architecture mistakes worth learning from.

Task comments — permissions, background loading and API pagination without COUNT
Part 22 of the series about building a Telegram task tracker in a tiny team. How task comments work in Boards: who can read, write and edit them, why they load in a separate background request, and how pagination works without a COUNT query.
Read →
Task change history — how we built an audit log in a task tracker
Part 20 of the series about building a Telegram task tracker in a tiny team. How Boards got a change history for tasks, why we write it explicitly in services in the same transaction as the changes themselves, and why EF Core interceptors, temporal tables and event sourcing did not fit.
Read →
Custom fields for tasks — EAV vs JSONB in PostgreSQL, a separate table per type and filtering by values
Part 19 of the series about building a Telegram task tracker in a tiny team. How we added attributes (custom fields) to issues, why we chose an EAV model with a separate value table per type instead of a JSONB column, and how filtering tasks by attribute values works.
Read →
Sequential issue numbers — a counter on PostgreSQL INSERT ON CONFLICT, space keys and organization URLs
Part 18 of the series about building a Telegram task tracker in a tiny team. How issues got sequential numbers like BRD-42, why a counter on INSERT ... ON CONFLICT DO UPDATE suited them better than a PostgreSQL sequence, and how space keys and organization URLs work.
Read →
Access control in a task tracker without roles — an alternative to RBAC, four versions of the model and space-level permissions
Part 17 of the series about building a Telegram task tracker in a tiny team. Why Boards has no roles, how the permission system went through four versions looking for a balance between flexibility and simplicity, and why permissions are now granted only on the organization and the space.
Read →
Multi-tenancy in a SaaS task tracker — organizations, a personal organization for every user and a separate organization JWT
Part 16 of the series about building a Telegram task tracker in a tiny team. How we added organizations to Boards: a shared-database data model, a personal organization for every user, joining by an invite code, and a separate JWT issued when a user enters an organization.
Read →
Spaces in a task tracker — grouping boards by project, the data model and moving existing users' data
Part 14 of the series about building a Telegram task tracker in a tiny team. How spaces group boards by project, why the space lives on the board and not on the issue, and the SQL migration that gave every existing user a default space.
Read →
When not to use a nullable foreign key — modelling the empty state as a default row
Part 9 of building a Telegram task tracker in a tiny team. The issue layer in the web app and the database design decision at its centre — when a nullable foreign key is the wrong choice and a dedicated default row is better, using issues, epics and the backlog as the example.
Read →
Deploying a .NET app and PostgreSQL to a cheap VPS with Docker Compose
Part 6 of building a Telegram task tracker in a tiny team. Deploying to a cheap VPS with Docker Compose — self-hosted PostgreSQL tuned for 1 GB of RAM, a GitHub CI pipeline running on push to main, and a walkthrough of the Dockerfiles.
Read →
Choosing a pet project stack for a tiny team — .NET, PostgreSQL, Nuxt, and why we prefer boring technologies
Part 4 of building a Telegram task tracker in a tiny team. The reasons behind .NET 10, PostgreSQL 18, Nuxt 4 and Vue 3. A bit about the MongoDB-to-Postgres migration in a past project that taught us to prefer boring, stable technologies.
Read →
EF Core Triggers in C# — How to Write Database Triggers With Fluent Syntax and Expression Trees
A deep dive into Laraue.EfCoreTriggers — how to define database triggers in C# using fluent syntax, how the library translates expression trees to SQL, and how to extend it for new database providers.
Read →