今日已更新 446 条资讯 | 累计 41220 条内容
关于我们

标签:#software

找到 560 篇相关文章

AI 资讯

Stop Scattering if (role === 'admin') Everywhere: A 3-Level Permission Tree for Page & Section Access

Most apps start their access control with something like this: function canEditReportsSummary ( role ) { return [ ' EDITOR ' , ' ADMIN ' ]. includes ( role ); } It works, right up until you have a dozen pages, each with a few sections, each needing independent read/write rules per role. Now you've got dozens of these little arrays scattered across the codebase, and adding a new role means hunting down every single one and hoping you didn't miss any. 0 There's a much simpler model that scales cleanly: a three-level permission tree — page → section → { r, w } - plus one generic function that walks it. No new library, no framework lock-in, just a data structure and ~5 lines of code. The shape of the data Instead of scattering role checks in code, define one permission tree per role . Three levels deep: Page — the top-level feature/route ( dashboard , reports , settings ) Section — a sub-area within that page ( overview , summary , billing ) Action — r (read) or w (write) { "dashboard" : { "overview" : { "r" : true , "w" : false }, "analytics" : { "r" : true , "w" : false } }, "reports" : { "summary" : { "r" : true , "w" : false }, "export" : { "r" : false , "w" : false } }, "settings" : { "general" : { "r" : true , "w" : false }, "billing" : { "r" : false , "w" : false } } } This one blob fully describes what a single role can see and do. Give each role its own tree, e.g. for three common roles: Page Section Viewer Editor Admin dashboard overview r r, w r, w dashboard analytics r r r, w reports summary r r, w r, w reports export – r r, w settings general r r r, w settings billing – – r, w Notice how this reads almost like a spreadsheet a product owner could fill in — that's the point. It's declarative data, not scattered if statements, so non-engineers can review it and engineers don't have to guess what a role does. The generic access-check function Once permissions are just nested objects, checking access is one small, reusable, framework-agnostic function: function

2026-07-22 原文 →
AI 资讯

The Overengineering Trap We All Fall Into

The most dangerous overengineering does not look careless. It looks thoughtful. It has clean interfaces, reusable components, configurable behavior, extension points, and an architecture diagram that makes the system appear ready for anything. Then the next feature arrives. A change that should take one afternoon touches seven layers, breaks three abstractions, and forces the team to understand a framework built for requirements that never appeared. That is what makes overengineering difficult to recognize. It rarely presents itself as unnecessary complexity. It presents itself as responsible engineering. It Usually Begins With a Reasonable Fear Developers do not overengineer because they want to make systems harder. They usually remember an earlier project that became painful. Maybe duplicated business logic spread across several screens. Maybe a component could not support a second use case. Maybe an integration became impossible to replace. Maybe a narrow implementation eventually required an expensive rewrite. The next time a similar problem appears, the team tries to protect itself. What if this feature grows? What if another team needs it? What if product asks for configuration? What if we add more providers? What if the rules change? These are reasonable questions. The problem begins when imagined requirements receive the same architectural weight as real ones. A single approval flow becomes a workflow engine. Two similar components become a universal rendering framework. One pricing exception becomes a configurable rules platform. The team tries to avoid future pain and creates immediate friction instead. The first use case now has to support requirements that do not exist. Developers must understand extension points nobody uses, configuration nobody needs, and interfaces protecting boundaries that have not appeared. Thinking about the future is not the mistake. Building the future before there is evidence is. Reuse Is Expensive Before the Pattern Is Stable

2026-07-22 原文 →
AI 资讯

REST API

I honestly thought learning REST APIs would be easy. At first, creating a simple GET or POST endpoint feels straightforward and you start thinking, "I've got this." Then reality hits. Every API needs middleware, validation, error handling, controllers, database integration, authentication, authorization, testing, pagination, CORS, environment variables, deployment and a dozen other things. Somewhere along the way, you realize you didn't just sign up to build an API—you signed up to build an entire backend ecosystem. 😂💻

2026-07-22 原文 →
AI 资讯

The future of AI coding isn't better prompts. It's better engineering constraints.

Over the past few months, I've noticed that most discussions around AI coding assistants focus on prompts. People share: .cursorrules AGENTS.md CLAUDE.md long prompt templates custom instructions The assumption is always the same: "If I explain my engineering practices clearly enough, the AI will follow them." For simple projects, that works. For real software projects, it eventually breaks down. The problem isn't intelligence. It's governance. Every AI coding assistant eventually produces something like this: giant functions skipped tests undocumented architectural decisions ignored security practices direct commits inconsistent commit messages missing pull request descriptions Not because the model suddenly became "worse". Because nothing prevents it from taking shortcuts. Exactly like humans. We already solved this problem... for humans. Professional software engineering has never relied on trust. Instead, we built systems that enforce discipline. We don't ask developers to: write tests We fail CI. We don't ask them to: use meaningful commit messages We reject the commit. We don't ask them: not to push directly to production Protected branches make it impossible. Engineering isn't based on trust. It's based on constraints. Yet with AI... ...we went backwards. Instead of constraints, we write instructions. We create increasingly sophisticated prompt files hoping the assistant will remember them. Always write tests. Always document architectural decisions. Use GitHub Flow. Follow OWASP. Keep functions below 40 lines. Never commit directly to main. Those aren't guarantees. They're suggestions. And suggestions are eventually ignored. Rules are not enforcement. Recently I came across an article making a simple observation: Rules without enforcement are just hopes. That sentence stayed with me. It perfectly describes the current state of AI-assisted development. An AI may fully understand your engineering rules. It may even agree with them. But unless something checks

2026-07-22 原文 →
AI 资讯

Why I Switched to Plain Text Accounting

Why I Switched to Plain Text Accounting Mint is shutting down. After 10 years of financial tracking, I am losing my data again. This time, I switched to plain text accounting with Beancount. The Problem with Traditional Apps Traditional financial apps have several issues: Export limitations : They only provide summary reports, not raw transaction data Proprietary formats : Data is stored in closed databases that require specific software to read Platform lock-in : Different systems are incompatible with each other Financial records are long-term. Over the past decade, dozens of financial apps have shut down, leaving users with years of records wiped out. The Plain Text Solution Plain text accounting with Beancount offers a different approach: Data Sovereignty Your data belongs to you. You can: Open it with any text editor Read it directly as a human Access it permanently, without depending on specific software Process it freely and completely Migrate with near-zero cost Future-Proof Plain text files will remain readable decades from now. They do not depend on any company staying in business or maintaining compatibility with legacy systems. Making the Switch The learning curve was worth it. I now have: Complete control over my financial data No vendor lock-in Confidence that my records will exist as long as I want them to Your data, your sovereignty. PersonalFinance #Fintech #DataSovereignty #Beancount

2026-07-22 原文 →
AI 资讯

Write Code You Can Still Read 6 Months Later

I'm AlanWu. I'm in junior high. I've written a lot of bad code. Here's what I changed to make it less bad. 1. Name things like a human // Don't do this int d ; // days? distance? damage? int cnt = 0 ; // "cnt" — you know, the classic vector < int > v ; // v of what // Do this int daysUntilDeadline ; int errorCount = 0 ; vector < int > studentScores ; Full words, no abbreviations. idx instead of i in loops is fine. But sz for size, cnt for count, ptr for pointer — just type the word. You're not being charged by the character. 2. Functions should do one thing If you need the word "and" to describe a function, split it. // Bad: does two things, name lies void loadAndValidateConfig () { readFile (); checkSyntax (); } // Better Config loadConfig ( string path ) { return parseConfig ( readFile ( path )); } bool validateConfig ( const Config & cfg ) { return cfg . width > 0 && cfg . height > 0 ; } My rule of thumb: if a function is longer than what fits on one screen, break it. If I can't describe what it does in one sentence without "and", break it. 3. Comments explain WHY, not WHAT // Bad — tells me what the code already says // Loop through all students for ( auto & s : students ) { s . score += 5 ; } // Good — tells me WHY, which the code can't // Extra credit: 5 points for submitting early for ( auto & s : students ) { s . score += 5 ; } If you're writing a comment that just restates the next line of code, delete it. The only comments worth keeping are the ones that answer "why did I do it this way?" 4. Don't nest too deep // Bad — 3 levels deep, I've already forgotten what the top level was for ( auto & student : students ) { if ( student . hasSubmitted ()) { for ( auto & answer : student . answers ) { if ( answer . isCorrect ()) { score ++ ; } } } } // Better — flatten with early exits for ( auto & student : students ) { if ( ! student . hasSubmitted ()) continue ; for ( auto & answer : student . answers ) { if ( ! answer . isCorrect ()) continue ; score ++ ; } } Al

2026-07-22 原文 →
AI 资讯

A IA não matou a Engenharia de Software. Ela a tornou mais importante do que nunca.

Durante anos fizemos a pergunta errada "Será que a IA vai substituir os desenvolvedores?" Hoje sabemos que essa não era a pergunta correta. A pergunta correta é: O que passa a ter valor quando escrever código deixa de ser caro? Isso muda completamente a engenharia de software. Por décadas, metodologias como Waterfall, Scrum, XP, DDD e Clean Architecture nasceram em um mundo onde escrever código era caro. Documentação envelhecia rapidamente porque reescrevê-la custava caro. Especificações eram abandonadas porque implementar consumia semanas. Então surgiu a IA. Pela primeira vez na história, produzir código ficou quase gratuito. O valor migrou. Código ficou barato. Julgamento não. Hoje qualquer LLM produz centenas de linhas de código em segundos. Mas ela não decide: qual problema resolver; quais regras de negócio existem; quais exceções importam; quais compromissos arquiteturais devem permanecer pelos próximos cinco anos. Essas continuam sendo responsabilidades humanas. O gargalo mudou Antes, escrevíamos código. Agora escrevemos decisões. Especificações. Arquiteturas. Critérios de aceitação. Revisões. A vantagem competitiva deixou de ser velocidade de digitação. Passou a ser clareza de pensamento. Por que o vibe coding não escala Conversas são uma péssima fonte de verdade. Cada prompt aumenta o contexto. Cada correção adiciona mais tokens. Cada interação obriga a IA a reconstruir sua intenção. Em algum momento ela deixa de raciocinar sobre o sistema e passa a raciocinar sobre a conversa. Esse é o verdadeiro custo escondido do vibe coding. Especificações passam a ser o centro do projeto Foi essa percepção que originou o Spec Driven Development . A ideia é simples: A conversa deixa de ser a memória do projeto. A especificação passa a ser. Cada funcionalidade nasce de uma spec, evolui para um plano, transforma-se em tarefas e somente depois é implementada. O resultado é previsibilidade. Menos retrabalho. Menos tokens. Menos ambiguidades. https://books.kodel.com.br/pt-br/

2026-07-22 原文 →
AI 资讯

Why your Clio token stopped working

If your Clio integration started returning 401 and you are trying to work out why, the first question is not about your code. It is which of Clio's two OAuth systems you are on, because they have different rules and most advice on the internet does not say which one it is describing. There are two, and they are not interchangeable Clio Manage is the older one. OAuth at app.clio.com/oauth , API at app.clio.com/api/v4 . Clio Platform is the newer one, covering Grow and the lead inbox among others. OAuth at auth.api.clio.com/oauth , API at api.clio.com . They differ on essentially every point that matters when a token dies. Manage Platform Access token lifetime 2,592,000s, 30 days 86,400s, 24 hours Refresh token expiry Documented as none No time expiry, but rotates Refresh token rotation Not documented, and the refresh sample returns no new refresh token Documented: rotates on every use, previous one revoked Revocation endpoint POST app.clio.com/oauth/deauthorize , Bearer auth POST auth.api.clio.com/oauth/revoke , Basic auth Treat the lifetime row as the documented default rather than a constant. Honour the expires_in you get back on each response instead of hardcoding 30 days, because there are field reports of accounts issuing much shorter access tokens, and a hardcoded assumption fails in a way that looks exactly like revocation. That rotation row is the one that decides your debugging. On Platform , every refresh gives you a new refresh token and kills the old one, so failing to persist the new value out of each response leaves you holding a dead token the next time you try. Clio's docs say it directly: store the new refresh token returned in each response. On Manage , the documented behaviour is a long-lived refresh token that does not expire and is not replaced. Their refresh response sample does not even include a refresh_token field. So on Manage, a token that suddenly stops working usually points somewhere else: revocation, or the wrong region. The region trap

2026-07-22 原文 →
AI 资讯

Why Every Developer Needs a Personal Website

Your resume tells people what you’ve done. Your GitHub shows what you’ve built. But your personal website tells people who you are. When I started learning web development, I believed that a good resume and a few GitHub repositories were enough. Like many students, I spent countless hours building projects, solving coding problems, and learning new technologies. Every new project felt like a milestone, yet all of them remained scattered across different platforms. A recruiter would have to open my resume, visit my GitHub, search for my LinkedIn profile, and perhaps never even discover the articles I had written or the experiments I had built. That made me realise something important. Developers need a place on the internet that they truly own. Not another profile. Not another social media account. A place that represents their identity, work, and journey. That’s what a personal website becomes. More Than Just a Portfolio Many people hear the words personal website and immediately think of a portfolio with a few screenshots and a contact form. A great developer website goes much further. It answers questions before anyone has to ask them. Who are you? What technologies do you enjoy working with? What problems have you solved? What kind of developer are you becoming? What have you learned recently? How can someone reach you? Instead of forcing visitors to jump across five different platforms, everything exists in one carefully designed experience. Your Name Deserves a Home Every developer works hard to build projects. Very few work equally hard to build their own identity. When someone searches your name, what should they find? Ideally, the very first result should be something you completely control. A website with your own domain isn’t just another webpage. It’s your digital home. Unlike social platforms, algorithms cannot redesign your identity overnight. You decide what visitors see first. You decide which projects matter. You decide how your story is told. Resume

2026-07-22 原文 →
AI 资讯

You can build it. Should you?

I've spent my career helping people build software. This is a series of letters about what happens when the tools for building change faster than the principles behind building. Each one is a reminder that while the technology changes quickly, the questions that matter often stay the same. -- Dear past Jenna, The thing that drew you to tech in the first place, that it's always changing, is the thing that will keep you here. Tools change (sometimes for the better and sometimes not) almost weekly. People who never consider themselves technical, much less a developer, will build apps in an afternoon with this new programming language called English. Products will go from idea to deployed before you finish your first cup of coffee (you have a toddler now, so you rarely get the full cup before it goes cold anyways). "I don't know how to code" or "I'm not technical" is no longer a barrier to building. And that's exciting, given you've focused nearly your entire career helping others build software. But we can't confuse the ability to build with the wisdom to build. One of the most valuable habits you've developed over the last two decades is asking a simple question: "Should we build it?" For most of your career, "Can we build it?" was a hard question. Time, budget, complexity, maybe the tech wasn't there yet. But those constraints were usually temporary. With enough people, time, and money, just about anything is possible. But the real questions were always: Should we build this? Is this the best use of our time? Does this solve a problem our customers actually need solved, or does it create new problems? What are we choosing not to build? Those questions haven't changed, but the environment around them has. Today, almost anyone can build software. Between tools like Lovable, Bolt, Replit, Claude Code, Codex, and whatever's next, the barrier to building software is lower than it's ever been. Now the question "Can we build it?" is too easy to answer. It's almost always ye

2026-07-21 原文 →
AI 资讯

How the V8 Engine Optimizes JavaScript at Runtime

.The V8 engine speeds up JavaScript by dynamically compiling frequently run bytecode into optimized native machine code. However, if you pass inconsistent argument types to these optimized functions, V8 panics and deoptimizes back to bytecode. Keeping your functions monomorphic (single-typed) prevents this costly deoptimization loop, ensuring maximum runtime execution speed. If you’ve spent as much time digging into V8 execution flags as I have, you quickly realize that JavaScript is constantly rewriting itself under the hood. We like to think of JavaScript as a dynamically typed scripting language. But at runtime, engines like V8 are working tirelessly to turn your code into a highly optimized, statically typed powerhouse. When we violate that type stability, we pay a massive performance tax. How does the V8 engine optimize JavaScript at runtime? V8 uses a multi-tiered compilation pipeline that starts with an interpreter for fast startup times, then upgrades hot functions to optimized machine code using a JIT compiler. By tracking runtime type patterns, the engine can safely make assumptions to skip expensive dynamic lookups. When I look at V8’s execution pipeline, I see two primary systems working in tandem: Ignition (the interpreter) and TurboFan (the JIT compiler). Initially, Ignition compiles your raw JavaScript into bytecode so your app can boot instantly. As this bytecode executes, V8 allocates a data structure called a Feedback Vector for each function. Inside this vector are Feedback Slots (managed by Inline Caches, or ICs). These slots act as recorders, capturing the exact types (or "shapes") of the variables passing through your code. Once a function runs frequently enough to cross an execution threshold, V8 marks it as "hot" and hands it to TurboFan. TurboFan reads those feedback slots, assumes the types will remain identical in the future, and compiles a highly streamlined, native machine code version of that function. What happens when you pass differe

2026-07-20 原文 →
AI 资讯

Learning Software Engineering in the Era of AI

Learning software engineering in the past was a straight forward process, you learn the programming language, you build projects in your portfolio, apply to companies, get a job and life goes on. Doing that in the current times might be a little bit different, as when applying for jobs, you can see some new requirements other than your programming skills and portfolio project such as Prompt Engineering, Work with Agents, Claude Code, and others. You may ask yourself, what are those? And if I am new to Software Engineering, will that change my learning path? Lets discuss all this below. Software Engineering in the Past For a long time, the path into software engineering was clear. You picked a language, maybe Java, Python, or JavaScript. You spent a few months learning the syntax, then the fundamentals: data structures, algorithms, how a database works, how the web sends and receives data. After that, you built things such as A todo app, weather app, clone of a website you liked. These projects went into a portfolio, usually a GitHub profile and a simple personal site. Then you applied to companies, passed a technical interview, and started your first job, so the skills you needed were stable, if you learned React in 2018, React was still useful in 2021. Tools changed, frameworks came and went, but the core idea stayed the same: you write the code, you understand what you wrote, and you fix it when it breaks. When Did AI Start Becoming Something Required The shift did not happen in one day. It came in steps. The first step was autocomplete , around 2021, tools like GitHub Copilot started suggesting the next line of code while you typed. Most developers saw it as a nice helper, nothing more. It saved you from writing boilerplate, but you were still the one thinking. The second step was chat , when ChatGPT and Claude became popular, developers started using them to explain errors, review code, and write small functions. Still a helper, but a much stronger one. At this

2026-07-20 原文 →
AI 资讯

A Practical Workflow for Contributing to a Large, Structured Codebase

This is the workflow I follow before I use AI agents to implement any feature or bug fix. 🧭 Requirements/Specification ↓ Design/Architecture ↓ AI Code Generation ↓ Human Review ↓ Build & Static Analysis ↓ Testing & Validation ↓ Defect Resolution ↓ Security & Compliance Review ↓ Release ↓ Production Monitoring vs Claude Code ↓ Implements feature ↓ Codex QA Agent ↓ Runs application ↓ Tests happy path ↓ Tests edge cases ↓ Tests error handling ↓ Produces QA report This will resolve the self-review bias, confirmation bias, or AI-to-AI bias. 1️⃣ Understand Before Writing Code Before touching any code, I try to understand what I'm building and why . I usually start by reading: specs/<module>/<TICKET>-<slug>.md plan/<module>/<TICKET>-<slug>.md status.md Then I review the project conventions: specs/CONVENTIONS.md specs/conventions/core-porting.md Finally, I read the existing implementation (entities, services, mappers, etc.) so my changes follow the existing architecture instead of introducing a new style. 💡 Pro-Tip Good code fits into the codebase. Great code looks like it was always there. 2️⃣ Plan the Change Once I understand the requirements, I identify which architectural layers are affected. I always respect the dependency order: Schema / Entities / DAOs ↓ Mappers / DTOs ↓ Service Layer ↓ Application Layer ↓ Controllers I don't jump ahead of dependencies. If a change is complicated or ambiguous, I document the approach before writing code. --- ## 3️⃣ Write the Code While implementing, I follow the repository's rules. Some examples: | Rule | Detail |---|---|---| | DTOs | Generated from `schema.yml` — never handwritten | | Status values | Sourced only from the Core Porting specification | | Traceability | Every ported behavior includes a source citation | Citation formats I use: - `← Source <path>` - `← PS §...` - `← BR-###` Beyond repository rules, I also try to: - ✅ Match existing naming conventions - ✅ Keep comments minimal and meaningful - ✅ Make small, focused chang

2026-07-19 原文 →
AI 资讯

LOD (Law of Demeter)

Introdução O nome do princípio vem do próprio nome do projeto de pesquisa (que remete a Deméter, deusa grega da agricultura — a metáfora era "cultivar" software que cresce de forma incremental e adaptável, não do princípio de acoplamento em si). O projeto Demeter investigava como reduzir o custo de manutenção de sistemas orientados a objetos observando que boa parte das mudanças de software quebrava código muito distante do ponto onde a mudança real acontecia — um efeito cascata causado por classes que conheciam profundamente a estrutura interna de outras classes. Essa observação foi confirmada empiricamente alguns anos depois: em 1994, Chidamber & Kemerer publicaram as famosas métricas CK ( A Metrics Suite for Object Oriented Design ), nas quais o CBO (Coupling Between Objects) — quão acoplada uma classe é a outras — se tornou um dos preditores mais fortes de defeitos e esforço de manutenção em estudos empíricos posteriores de engenharia de software. Ou seja: a intuição por trás da Law of Demeter (menos acoplamento = menos bugs ao mudar código) tem respaldo em dados de décadas de pesquisa empírica em qualidade de software. Definição Também chamada de "Principle of Least Knowledge" , a formulação clássica é: Um método M de um objeto O só deve chamar métodos de: O próprio O Os parâmetros recebidos por M Qualquer objeto que M crie/instancie internamente Os componentes diretos de O (seus atributos/campos) Variáveis globais acessíveis a O Resumo popular: "use apenas um ponto" — evite código como: pedido . getCliente (). getEndereco (). getCidade (). getNome () Isso é conhecido como "train wreck" (trem de vagões) — cada . é um vagão acoplado ao anterior. Se a estrutura interna de Cliente ou Endereco mudar, todo código que fez essa travessia quebra, mesmo estando em um módulo completamente não relacionado. Porque isso importa na prática? Quando o método M faz objeto.getX().getY().metodo() , ele passa a depender da estrutura interna de X e Y , não só da interface pública d

2026-07-19 原文 →
AI 资讯

LLD Domain Modeling: How to Debug Your Design When It Feels “Wrong”

Every engineer eventually hits this phase: “My design looks okay… but something feels off.” No compile errors. No obvious bugs. But still: responsibilities feel scattered services feel too big entities feel too thin logic feels duplicated boundaries feel unclear This is normal. Because domain modeling is not about getting it right in one attempt. It is about refining structure until the business behavior becomes clear. Step 1 — Start With the Symptom, Not the Code If your design feels wrong, don’t immediately rewrite everything. First identify the symptom: Common symptoms: too many “Manager” services logic repeated in multiple places unclear ownership of rules too many dependencies between modules frequent “if-else explosion” Each symptom points to a specific modeling issue. Step 2 — Check If Invariants Are Scattered Ask: “Where are my business rules living?” Bad sign: Rules inside services + controllers + helpers This leads to: inconsistent behavior duplicated validation broken business guarantees Good design: invariants live close to the entity or aggregate root Step 3 — Check Entity vs Service Confusion A very common issue: Entities become dumb: only fields no behavior Services become overloaded: all logic all rules all decisions This creates: Anemic Domain Model + Fat Services Fix mindset: Entity = owns behavior + protects state Service = coordinates workflows Step 4 — Check Your Aggregate Boundaries Ask: “What must stay consistent together?” If your answer is unclear, you likely have: wrong aggregates or missing aggregates Example problem: Cart and Order sharing logic This causes: inconsistent pricing unclear lifecycle ownership Fix: Cart = intent Order = truth Step 5 — Look for “Hidden Coupling” Hidden coupling happens when: one module depends on internal state of another multiple services modify same data business rules are duplicated across boundaries This leads to fragile systems. Strong design ensures: each domain owns its own truth. Step 6 — Validate Stat

2026-07-18 原文 →
产品设计

Why Search Isn't Enough for Team Docs — What We Learned Building a Knowledge Graph Layer

Every team doc tool promises "search everything." Ours did too — and it still didn't answer the question new hires actually ask: not "where is this doc," but "why does this decision look the way it does, and what else does it touch?" We spent the last few months trying to solve that by treating team docs less like a filing cabinet and more like a graph. Here's what we tried, what broke, and what we'd do differently.

2026-07-18 原文 →
AI 资讯

The Missing Row: Auto-Provisioning Derived Records Without the Race Condition

Why some records should be created by your system, not your users, and how to do it safely in .NET. A support ticket lands on your desk: "The Teams page is empty. I added a member, but no team shows up." You check the API. It's behaving exactly as written: { "items" : [], "totalCount" : 0 } Nothing is broken. And that's the problem. The system is faithfully returning nothing, because the row that the page reads from was never created. Somewhere in your design, you assumed a human would create it first. This article is about a small, recurring design decision that quietly causes empty dashboards, confused users, and "is this a bug?" tickets: who is responsible for creating derived records the user, or the system? and how to let the system do it without introducing duplicate rows or race conditions. The problem Let's use a fictional product: a collaboration tool called Loop . In Loop, the important entities are: An Organization (a paying customer). A Member (a person invited into an organization under a plan). A Team a grouping that members belong to, keyed by (OrganizationId, PlanCode) . The admin dashboard lists Teams . Each team card shows a member count. Here's the catch in the original design: creating a Member wrote a member row. Creating a Team was a separate, manual step an admin was expected to do first. If an admin invited members without first creating the matching team, the dashboard showed nothing even though the members clearly existed. From the user's point of view, they did everything right. From the system's point of view, a required row simply didn't exist. Why it matters The Team record isn't independent information. It is fully derivable from the first member invited under a plan. When one entity's existence is implied by another, forcing a human to create it manually is a design smell. It leads to: Empty states that look like outages. Users can't tell "no data" from "misconfigured." Support load. Every skipped step becomes a ticket. Silent data dr

2026-07-18 原文 →