AI 资讯
Building RecipeHub: My Experience Developing and Deploying a Modern Recipe Sharing Platform with Django
As part of my learning journey with Django, I wanted to build a project that would challenge me beyond the basics. I decided to create RecipeHub, a web application where users can create, manage, and share recipes while exploring recipes from other users. The project started from a Django starter template, but I customized it by adding new features, redesigning the interface, and deploying it online. Features RecipeHub allows users to: Register and log in Create, edit, and delete recipes Browse recipes by category Save favourite recipes Upload recipe images Access a personal dashboard Use the application in both light and dark mode The application is fully responsive, making it easy to use on both desktop and mobile devices. Technologies Used I built the project using: Python Django Django Allauth PostgreSQL Tailwind CSS DaisyUI HTMX Vite Gunicorn Render GitHub was used for version control throughout the project. Challenges One of the biggest challenges was deployment. While everything worked locally, deploying to Render required configuring PostgreSQL, environment variables, and static files correctly. I also encountered an issue with uploaded recipe images. Since the application is hosted on Render's free tier, uploaded media is stored on an ephemeral filesystem, meaning uploaded images are lost after redeployment. Learning why this happens gave me a better understanding of the difference between development and production environments. Another challenge was redesigning the dashboards. I wanted them to feel clean and modern instead of looking like a default Django application, so I spent time improving the layout, spacing, and responsiveness. What I Learned This project helped me improve my understanding of: Django project structure Authentication and user management CRUD operations Database relationships Responsive UI design Git and GitHub workflows Deploying Django applications Debugging real-world issues More importantly, it taught me how to troubleshoot proble
AI 资讯
I Built a Manga Reader That Works on Every Platform --Here's How
I Built a Manga Reader That Works on Every Platform — Here's How Nyora is a free, open-source manga/manhwa/manhua reader for Android, iOS, macOS, Windows, Linux, Web, and even Docker — with AI-powered on-device translation and cross-platform sync. The Problem Every manga reader makes you choose: Free but ad-riddled (most Android readers) Polished but paywalled (commercial apps) Powerful but single-platform (Tachiyomi, Aidoku) I wanted one library — same titles, same progress, same bookmarks — on my phone, laptop, and browser. No ads. No account required. So I built it. What Nyora Does Every Platform, One App Platform Distribution Android APK (sideload) iOS/iPadOS IPA via AltStore/SideStore macOS .dmg or brew install --cask nyora Windows .exe (x64 + ARM64) Linux .deb , .rpm , or curl installer Web web.nyora.xyz — zero install Docker Single container, self-hosted No account needed to read. Cloud sync is opt-in. AI Translation That Understands Manga This is the flagship feature. Instead of dumping translated text over the artwork: Detects text baked into speech bubbles and captions Translates using on-device ML Typesets the result back over the original artwork Each platform uses the best local engine: Android : Google ML Kit + ONNX Runtime iOS : Apple Intelligence + Google Translate macOS : Apple Vision + MangaOCR CoreML Windows : Windows OCR Linux : Tesseract There's also an Ensemble AI Narrative Engine that tracks character names and speaking styles across chapters so translations stay consistent. 1,100+ Sources The Android app pulls from 1,100+ manga sources via 35 generic engine templates (Madara, FoolSlide, MMRCMS, etc.). Web has ~390 live, health-checked sources. Desktop ports are growing toward parity. Free Cloud Sync Sync library, categories, reading history, bookmarks, and exact page progress across all six platforms. Two sign-in methods: Google OAuth Nyora Cloud (email + password, free) Self-hostable — the backend is just Supabase/PostgreSQL with row-level s
AI 资讯
The AI Can't See What It Drew
Originally published on hexisteme notes . A while back I wrote about why your vibe-coded app looks worse than you expect. That post diagnosed the cause. This one is the fix that actually worked, on a real job: redesigning the mascot in my trip expense-splitting app. The mascot is the face of the app. It shows up in more than twenty places — onboarding, settings, the stats screen, the map, the diary, the settlement report, and five little mini-games. And it was nothing. One circle did double duty as head and body. No legs. No hands. No eyebrows. One X for an eye. Visually its identity was zero: a tinted circle. I knew it was bad. What I could not do was say what to change. Words don't converge on a picture I kept talking myself in circles about it, and so did the AI I was pairing with. Rounder? Add a hat? Bigger eyes? Every sentence sounded reasonable and none of them moved the decision. At some point I noticed what was actually going on: this was not a shortage of information. Nobody needed to go fetch a fact. It was a shortage of fidelity . A visual decision cannot converge in prose, because prose is not the medium the decision lives in. That is the tell. When a discussion loops and more words don't help, you don't need more analysis — you need a picture. So I stopped arguing and built prototypes. Three variants, not more tints The rule I gave myself: make variants that are structurally different, not palette swaps. Different silhouette, different anatomy, a different device carrying the identity. Repainting the same shape in different colors teaches you nothing. Three genuinely different creatures force a real choice. I built three and rendered every one as an action sheet so I could look at them side by side: A, a jelly bean. The safe evolution of what I already had. It slots into the UI cleanly, but its whole identity hangs on a single coin floating over its head. Shrink it and it's just a round blob again. B, a wallet. Object personification: a wallet body with
AI 资讯
We Don’t Have a Software Engineering Problem. We Have a Platform Engineering Problem.
Last month I set out to build a new product, and after a full week I had shipped exactly zero features. Not because I was slow. Not because the work was hard. Because before anyone could write a single line of business logic, my team had to re-decide a dozen things our company should have settled years ago . I've been a full-stack developer for almost five years — long enough to have worn most of the hats: WordPress developer, QA, frontend, backend, solution architect, founding engineer. I've built more than twenty web applications and a handful of mobile ones. Some of them I'm genuinely proud of: systems that poll PLCs every five seconds to watch over industrial equipment, a cybersecurity dashboard that mapped attacks across the world in real time using tree-based graphs, an OTT platform that streamed live events — including FIFA — to millions of concurrent viewers. Today I work on enterprise supply-chain finance software. So when I tell you the hardest part of that new product had nothing to do with code, I know how it sounds. Let me explain. The week that disappeared The experiment was ambitious on purpose. I wanted to build the new application — eventually a microfrontend inside a larger enterprise platform — but I didn't want to write most of it myself. I wanted Claude Code to implement while I acted as the architect: review, test, challenge, refine, repeat. That part worked. The AI wasn't the bottleneck. The bottleneck was everything that came before the first feature. Should we use React? Vite? Keep Create React App because the parent app still runs it — or migrate both? How does the parent consume the child, and does local development still work? Does authentication still work? Does routing? Do we adopt TypeScript when the existing app doesn't, knowing that splits one product into two standards? The app also had to feel native to the existing product — same spacing, typography, colors, interactions — except the company had a component library, not a design s
AI 资讯
# 📓 TanStack Query: Core Concepts & Summary
1. Introduction: What is TanStack Query? TanStack Query (formerly React Query) is a framework-agnostic state management library designed specifically to manage Server State —handling data fetching, caching, background updating, and cache invalidation. 2. Server State vs. Client State & The Memory Reality Client State: Owned and controlled entirely by the browser (e.g., isModalOpen , selected UI theme). Server State: Owned by the remote backend database (e.g., user profiles, posts, cart items). The browser only holds a read-only temporary snapshot . Where is data physically stored? Physical Location: By default, cached data lives strictly in the browser tab's JavaScript RAM (In-Memory) . Backend ("The Server"): Refers to your remote API/database (regardless of whether it runs on Kubernetes, Docker containers, serverless functions, or bare metal). Optional Persistence: You can opt to sync this RAM cache to localStorage , sessionStorage , or IndexedDB using TanStack Query Persisters. import React , { useState } from ' react ' ; import { QueryClient , QueryClientProvider , useQuery , useMutation , useQueryClient , } from ' @tanstack/react-query ' ; // 1. Initialize QueryClient (manages the RAM cache) const queryClient = new QueryClient ({ defaultOptions : { queries : { staleTime : 10000 , // Data stays fresh in RAM for 10 seconds }, }, }); // Mock API functions async function fetchPost ( postId ) { const res = await fetch ( `[https://jsonplaceholder.typicode.com/posts/$](https://jsonplaceholder.typicode.com/posts/$){postId}` ); if ( ! res . ok ) throw new Error ( ' Network error ' ); return res . json (); } async function createPost ( newPost ) { const res = await fetch ( ' [https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts) ' , { method : ' POST ' , headers : { ' Content-Type ' : ' application/json ' }, body : JSON . stringify ( newPost ), }); return res . json (); } // 2. Query Component (Fetching & Reading Data) function PostViewe
开发者
Slices Beyond the Basics
Hey Techie! 🌸 Welcome to my Go series! I'll be sharing what I'm learning in ways that make sense to me, the mistakes I make and the "aha!" moments that help everything click. Whether you're learning Go too or just curious about it, I hope you'll pick up something along the way. Feel free to add any insights or experiences in the comments. Today's topic is... drumroll, please! Slices . Let's dive in! So, what exactly is a slice? When I first came across slices in Go, I thought they were another name for arrays. Turns out, they're not! A slice is internally represented by a small data structure called a slice header . Instead of storing the elements themselves, the slice header stores a pointer to the underlying array, along with its length and capacity . This realization helped me understand why modifying a slice can also modify the original array. Another interesting and convenient thing is how flexible slices are compared to arrays which are fixed size. Slices can grow using functions like append() or be resliced to work with a smaller portion of the underlying array. This flexibility is one of the reasons slices are used so frequently in Go.
AI 资讯
Introducing Angular support for CopilotKit: bring any Agent into your app
Angular apps can now run any agent, with the streaming, tool calls, and shared state already handled. Today we're releasing Angular support for CopilotKit , an open source client that brings any AG-UI agent into your Angular app. It's built with Angular's own patterns, standalone components, dependency injection and signals. You get the building blocks for agent-native apps in Angular: pre-built chat components or a fully headless setup, generative UI, shared state, human-in-the-loop, multimodal attachments, threads and more. Use the CLI to scaffold a full starter Angular app with a Google ADK agent. npx copilotkit@latest init --framework adk-angular Let's see how to set everything up, then go through each of the pieces and give your agent the context. Quickstart docs are on docs.copilotkit.ai/angular . Rainer Hahnekamp (Angular GDE, NgRx core) and Murat Sari helped build the integration and are now taking on its ongoing maintenance. How everything fits together Everything runs on Agent-User Interaction Protocol (AG-UI) , the open protocol that connects agents to user-facing apps. It streams an agent's entire lifecycle as events, the messages, the tool calls, the state changes, which is what keeps your Angular app and the agent in sync. That matters because the agent becomes a choice you can change. The runtime can point at a BuiltInAgent , LangGraph, Google ADK, Mastra, Pydantic AI, Claude Agents SDK or any framework that speaks AG-UI and your Angular code doesn't change. Here's the architecture. ┌──────────────────────────┐ ┌──────────────────────────┐ │ ANGULAR APP │ │ COPILOT RUNTIME (Node) │ │ │ │ │ │ provideCopilotKit() │ ─────► │ holds your model keys │ │ <copilot-chat /> │ AG-UI │ connects to your agent │ │ tools · context · state │ ◄───── │ streams events back │ └──────────────────────────┘ └──────────────┬───────────┘ │ ▼ ┌───────────────────────────┐ │ YOUR AGENT + MODEL │ │ LangGraph · ADK · Mastra │ │ OpenAI or a local model │ └─────────────────────────
AI 资讯
QCon AI New York 2026: Registration Opens for December 15-16 Production-AI Conference
QCon AI New York 2026 (Dec 15-16) has opened registration at The Westin Jersey City Newport. Six tracks on production AI, chaired by Eder Ignatowicz with Faye Zhang and Wes Reisz. First sessions announced in August, full program by November. By Artenisa Chatziou
开发者
Google Turns a Selfie Video Into Your Account’s Spare Key
The next time you’re locked out of your Google account, you can use your face as part of the account recovery process.
AI 资讯
Pulling Business Rules Out of Your Service Isn't a Rewrite. It's a Seam.
Teams treat "externalize the rules" as two different decisions depending on the stack. In Node, it's "which npm package handles conditionals." In Java, it's "do we adopt Drools." Both framings are wrong in the same way — they turn an architecture decision into a product decision before anyone's actually designed the seam. The seam is the same regardless of language: rules become data instead of control flow, the boundary between your service and the rule layer is typed on both sides, and a contract test catches the moment a rule change would silently break what your code expects back. Get that seam right and it barely matters whether you're calling it from Express or Spring Boot. Get it wrong and you've just moved your if statements into a config file and called it progress. The seam: rules as data, not control flow The actual pattern is small. Instead of branching logic living inline in a handler or a service method, you define a typed input, a typed output, and a rule set that maps one to the other — evaluated somewhere the calling code doesn't need to know the internals of. That's it. That's the whole architectural move. Everything else — which engine, which language, how it's hosted — is an implementation detail on top of that seam. Most of the friction teams run into with a low-code layer in Node.js or a Java service isn't the rule engine choice. It's skipping the typed boundary and finding out three months later that a rule change silently returns a shape the calling code wasn't built to handle. Node.js: keeping the boundary typed Here's the seam in a TypeScript service. The route handler never sees a conditional — it sees a typed input going in and a typed decision coming out: interface PricingInput { userTier : " free " | " pro " | " enterprise " ; cartTotal : number ; couponCode ?: string ; } interface PricingDecision { discountPercent : number ; reason : string ; } async function evaluatePricingRule ( input : PricingInput ): Promise < PricingDecision > { c
AI 资讯
How to Import JSON into MongoDB and Export to CSV with Data Masking
Every morning, an online store receives the previous day’s orders from a marketplace partner. The file comes in JSON format. The company needs to add those orders to its main MongoDB orders collection. The sales manager also needs a CSV report that can be opened in Excel. That sounds like a small task. Import the file, copy the documents, export the report. But in practice, a few things can break the process. A date can be imported as a string. A field can have the wrong name. One batch may use total , while the main collection uses totalAmount . A temporary collection can keep old records and trigger duplicate key errors. A CSV export can create null values because the mapping points to fields that do not exist. And then there is customer data. The manager may need the sales numbers, but they probably do not need real customer names or internal customer IDs. This article walks through a real daily workflow: Import marketplace JSON ↓ Store the batch in a temporary MongoDB collection ↓ Copy the orders into the main orders collection ↓ Mask customer fields during export ↓ Create a CSV report The goal is not just to move data from JSON to CSV. The goal is to make the process repeatable, easier to check, and safer to share. The workflow The workflow has three jobs: Import Yesterday Orders ↓ Add Orders to Main ↓ Export Daily Sales Report The important part is the parent relationship between the jobs. Add Orders to Main depends on Import Yesterday Orders , so it only runs after the JSON file is imported successfully. Export Daily Sales Report depends on Add Orders to Main , so the CSV is created only after the main orders collection has been updated. This prevents the report from being generated when data is missing or incomplete. The incoming JSON file The partner sends a file with yesterday’s completed orders. A single order looks like this: { "orderId" : "ORD-2026-07-201" , "customerId" : "CUST-1003" , "customerName" : "Sofia Rossi" , "orderDate" : "2026-07-21T08:20:00
AI 资讯
Inertia and API responses living together in harmony
I love InertiaJS to the point where it's becoming a personality trait I tend to want to use it for everything, but adding Inertia to an existing Laravel API gets awkward fast. Same thing happens in the other direction: you start with a full Inertia frontend and then realize you want to expose some of that data as a public API too. The naive solutions are: Sprinkle if ($request->wantsJson()) into your controllers Maintain two separate routes that return the exact same data Neither feels right. So I made inertia-split Starting fresh with Inertia: serve both from the same controller class ProjectController extends Controller { use HasHybridResponses ; public function index () { return $this -> respond () -> component ( 'Projects/Index' , [ 'projects' => Project :: all (), ]); // Inertia request → renders the Svelte/Vue/React component // API request → returns JSON } } The controller doesn't check anything. Inertia requests get an Inertia response, API clients get JSON. Existing API? Don't touch it If you just want to make an existing API method Inertia-aware, one annotation is enough: #[InertiaComponent('Users/Show')] public function show ( User $user ): array { return [ 'user' => $user ]; } The method body stays exactly as it was. Inertia requests get the component rendered with your data as props. Everything else gets the same JSON as before. Methods without the annotation are completely unaffected. Wait, how does this even work? The package can out Inertia's ResponseFactory for its own in the service provider (opt-in): $this -> app -> singleton ( ResponseFactory :: class , HybridResponseFactory :: class ); // checks if it's an Inertia request and returns appropriate response Good old OOP. Thank you polymorphism. Wrap-up Whatever the direction of your problem, making Inertia and API endpoints use the same controller is a big win. You're still responsible for writing routes and wiring middlewares, but this should save a lot of time and effort. Still in beta, use accor
AI 资讯
Bizbox Build Log — Week of 2026-05-31
Shipped this week Workflows are now a first-class Bizbox primitive — PR #86 · v2026.603.0 The biggest drop this week. @DennisDenuto landed Workflows as a company-scoped concept that sits alongside issues and routines — not shoehorned into either. What that means in practice: Google ADK-backed execution — workflow pipelines run as ADK agents, with phase state persisted as run records. Human handoffs baked in — pipelines can pause and wait for a human before resuming. Deliverables that survive — artefacts from each run are persisted and surfaced in the UI. A pipeline graph in the UI — topologically ordered, showing live phase state and console output. This is the foundation. More on what we can build on top of it below. Workflow human-handoffs now route through ClickUp — PR #91 · v2026.605.0 The day after Workflows landed, @angelofallars wired up the last kilometre: when ADK Python code calls input() inside a pipeline, Bizbox now intercepts that call and sends a ClickUp message to collect the human reply — instead of blocking the process forever. A few things that were fixed along the way: input() monkey-patching now works consistently across Python environments (was silently failing in some setups). Failed workflow runs no longer submit deliverables. You only see artefacts from runs that actually completed. ClickUp awaiting-human bridge adapter ships as a pure plugin — PR #78 · v2026.601.0 This one technically crossed the line on the last day of May (23:56 UTC, 31 May), so it's in scope. @ralphbibera ported the ClickUp transport and adapter as a genuine plugin — implementing the AwaitingHumanBridgeAdapter registry interface — without touching bridge core at all. What that gives you: ClickUp works through the same provider-agnostic layer as any future provider (Slack, Discord, whatever comes next). The core doesn't know ClickUp exists. Included: send/poll/reaction transport, message templates for request_confirmation and ask_user_questions interactions, brain_is_think
AI 资讯
Introducing NumPy4J: Bringing NumPy-Style Computing toJava
Java is everywhere in backend systems, enterprise applications, and production environments. But when it comes to numerical computing, data manipulation, and scientific-style operations, Python's NumPy ecosystem has become the standard. I wanted a similar experience in Java: a lightweight, dependency-free library for working with multidimensional arrays and linear algebra. That idea became NumPy4J. What is NumPy4J? NumPy4J is an open-source numerical computing library for Java inspired by NumPy. It provides: Multidimensional arrays (NDArray) NumPy-style broadcasting Array creation utilities Reshaping and slicing Element-wise operations Linear algebra operations Example: NDArray A = NDArray . of ( new double []{ 1 , 2 , 3 , 4 }, 2 , 2 ); NDArray B = NDArray . ones ( 2 , 2 ); NDArray C = A . add ( B ); Matrix operations: NDArray result = LinearAlgebra . matmul ( A , B ); Solving equations: NDArray x = LinearAlgebra.solve(A, b); Why build another numerical library? There are already excellent Java math libraries available. The goal of NumPy4J is different: Provide a NumPy-like API experience Make multidimensional arrays a first-class concept in Java Keep the API simple and approachable Create a foundation for future scientific computing features Testing approach To make sure behavior stays consistent, NumPy4J uses Python NumPy as a reference implementation. Test cases are generated with NumPy and validated against the Java implementation, covering: Broadcasting Matrix operations Reshaping Transpose Linear solving Element-wise calculations What's next? The roadmap includes: More NumPy-compatible operations Matrix decompositions (QR, LU, Cholesky) Eigenvalue computation More statistics functions Performance improvements Try it out If you work with Java and need NumPy-style numerical operations, I would love for you to try NumPy4J, provide feedback, and contribute ideas. GitHub: https://github.com/darius1973/numpy4j Documentation: https://darius1973.github.io/numpy4j/inde
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
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
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. 😂💻
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
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
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