How to Grow From Senior to Staff Engineer
submitted by /u/gregorojstersek [link] [留言]
找到 1357 篇相关文章
submitted by /u/gregorojstersek [link] [留言]
Cutting OpenAI Costs From Scratch: What Nobody Tells You Three months ago I sat down with my finance lead and watched her scroll through our OpenAI invoice. The number was $14,200 for the month. That was the moment I knew we had a problem. Not a "maybe we should optimize" problem — a real, existential, "this kills our margins before we hit Series B" problem. I run a B2B SaaS platform that does a lot of LLM-powered document processing. Summarization, extraction, classification, the boring stuff that makes real money but burns tokens like crazy. We were routing everything through GPT-4o because, honestly, it was the path of least resistance when we started. Then the bills started arriving. This is the story of how I cut our LLM spend by 97%, the architecture decisions that made it possible, and the things I wish someone had told me before I started. The Math That Made Me Sweat Let me put actual numbers on the table. Here's what I was paying versus what I pay now: Model Provider Input $/M Output $/M vs GPT-4o GPT-4o OpenAI $2.50 $10.00 — GPT-4o-mini OpenAI $0.15 $0.60 16.7× cheaper DeepSeek V4 Flash Global API $0.18 $0.25 40× cheaper Qwen3-32B Global API $0.18 $0.28 35.7× cheaper DeepSeek V4 Pro Global API $0.57 $0.78 12.8× cheaper GLM-5 Global API $0.73 $1.92 5.2× cheaper Kimi K2.5 Global API $0.59 $3.00 3.3× cheaper Look at that DeepSeek V4 Flash row. 40× cheaper than GPT-4o. For comparable quality on the workloads I was running. I had been leaving 97.5% of my budget on the table. Doing the mental math: a $500/month OpenAI bill becomes $12.50. My $14,200 bill? Theoretically $355. That's not optimization, that's a different business. Why I Almost Didn't Do It Here's the thing nobody tells you about cost optimization at a startup: it's not a technical problem, it's a willpower problem. The reason I was paying OpenAI 40× too much wasn't because their API is hard to use. It was because switching felt risky. I had deadlines. I had a roadmap. I had investors asking about g
Originally published on the Keylight blog . A short founder note on why Keylight exists. Every product starts as somebody's unsolved problem; this is mine, and if you are shipping a paid app you have probably run into the same one. The problem I kept hitting I wanted to sell a desktop app directly. Not through the App Store — directly, to customers I could actually talk to. The payment side was easy: Stripe is excellent and the decision took an afternoon. Then I got to licensing, and everything slowed down. Stripe takes the money. It does not give you a license key. It does not sign anything your app can verify. It does not know what a device activation is. The moment a customer has paid, you are on your own: you need to mint a key, sign it so it cannot be forged, deliver it, let the app check it, track devices, and revoke it on a refund. None of that is payment processing, so none of it is in Stripe. So I looked at the platforms that do bundle licensing. Why the merchant-of-record platforms did not fit Paddle, Gumroad, and Lemon Squeezy all advertise license keys. I looked hard at each, and the same three problems came up. The fee. As merchants of record they charge around 5%, against Stripe's ~2.9%. On every sale, forever. Reasonable if it solved my problem well — but it did not. Offline validation. This was the dealbreaker. Their licensing is built around an online validation API: to check a key, the app calls the platform's server. My app is a desktop app, and desktop apps run on planes, behind firewalls, and offline. An online-only check leaves no good option. Fail closed — refuse to run without a server response — and a paying customer who is simply offline cannot use what they bought. Fail open — keep running when the server is unreachable — and the check is trivially bypassed: block the app's network access and it can never re-check the license or learn it was revoked. The app never actually verifies anything itself; it only knows what the server last told i
Originally published on the Keylight blog . The thing that stops developers from moving their licensing isn't the work. It's the fear of one specific moment: a paying customer opens the app after you've switched, and it tells them they're unlicensed. That's the nightmare — you reach for lower fees and customer ownership, and the bill comes due as a wave of "I already paid for this" support tickets. It's a reasonable fear, and it's also avoidable. Migrating onto Keylight doesn't require invalidating anything, re-issuing anything, or asking customers to do anything. This post is about the one rule that keeps everyone working, the two situations you might be in, and why a scary-sounding "major version" jump changes none of it. When you're ready for the click-by-click mechanics, the companion piece covers them: How to Import an Existing Customer Base into Keylight . Why migrating licensing feels risky A license check is binary in the moment a customer experiences it: the app either lets them in or it doesn't. So any change to the system behind that check feels like it's playing with a live wire. Switch the layer that answers "is this person allowed in," the thinking goes, and you risk every existing customer getting the wrong answer at once. That instinct is right about the stakes and wrong about the mechanism. The wave of lockouts people picture comes from one specific mistake: treating migration as a cutover , where the old keys stop being recognized the instant the new system goes live. If your migration invalidates the old keys, yes — everyone breaks. The entire trick is to not do that. The one rule: old keys stay valid Here's the rule the whole migration hangs on: you bring your customers' keys in as they are, and nothing gets invalidated. When you import an existing customer, their license is a live, active record from the first second. If you include the key string they already have, that key is what Keylight stores — not a replacement. So when your new build ask
Originally published on the Keylight blog . "Should I charge once or charge monthly?" is one of the first real decisions an indie app faces, and it is usually answered by copying whoever the founder admires rather than by what fits the product. Both models are legitimate. This post lays out when each one actually makes sense, the honest tradeoffs, and how Keylight models perpetual keys and renewing subscriptions so the licensing follows your pricing instead of constraining it. The two models, defined A one-time (perpetual) license is a single payment for a license that does not expire. The customer owns that version — and usually some agreed window of updates — forever. Think of the classic "buy version 3, use it as long as you like" desktop app. A subscription license is a recurring payment for continued access. The license is valid while the customer keeps paying; stop paying and access ends or degrades. The recurring revenue funds ongoing development and any server-side costs the app carries. The distinction is not about the dollar amount — it is about what the customer is buying: ownership of a thing, or ongoing access to a service. Get that framing right and the model usually picks itself. When a one-time license is the right call A perpetual license fits when your app is a tool the customer owns and runs locally , with low ongoing cost to you per user. A focused Mac utility, an audio plugin, a developer tool that does its job on the user's machine — these have little marginal server cost, so charging rent for access is hard to justify and customers feel it. One-time pricing also builds trust. There is no metering, no "what happens if I stop paying," no fear of being locked out of work they already did. For tools people depend on, that ownership feeling is a genuine selling point, and it is exactly the kind of no-value-extraction stance that earns goodwill with developers and power users. The tradeoff is honest: revenue is lumpy and front-loaded. You get paid o
A Go interview is rarely about syntax alone. Yes, you should know how slices work, how goroutines...
Hello Devs, for months, I felt confident about system design interviews. I'd watched endless YouTube videos. I'd studied architecture diagrams. I could explain how Netflix builds recommendation systems. I understood Kafka, Redis, load balancers, and microservices. I'd memorized the designs of Twitter, Uber, YouTube, and TinyURL. Then I sat down for my first real system design interview and froze. The interviewer asked: "How would you design a notification system?" I had memorized notification systems. I knew about push notifications, email queues, delivery workers, and retry logic. I could recite architectural patterns. But suddenly, none of that helped. I didn't know which questions to ask first. I started designing before understanding the actual requirements. I built architecture for problems that didn't exist. I missed obvious bottlenecks. I couldn't articulate why I made specific trade-offs. When the interviewer pushed back, I had no framework to adjust. I failed that interview. But that failure taught me something crucial: System design interviews aren't about knowing technologies. They're about knowing how to think. After that, I went back and systematically practiced 20 system design problems. Not passively watching solutions. Actually designing. Making mistakes and refining my approach. And somewhere around problem 12, a pattern emerged. The best candidates didn't know more technologies than anyone else. They had a framework . They asked the same questions in the same order. They structured their thinking consistently. They could handle curveballs because their framework was flexible. They reasoned through trade-offs explicitly. Here's the framework that finally made it click for me. The Problem with Memorization Before I share the framework, let me explain why memorizing designs fails. When you memorize " How to Design Twitter," you learn: Use relational databases for users and tweets Use NoSQL for timelines Cache with Redis Use message queues for fanout S
You built a desktop app — macOS, Windows, Linux, native or Tauri/Electron — and you want to sell it directly instead of handing 15–30% to Apple or Microsoft. Selling outside the stores means you keep the margin and own the customer relationship. It also means the plumbing the stores quietly handled is now yours: distribution, payments, licensing, updates, support. Here's the whole path, in roughly the order you'll hit it — with the licensing part (the one most people underestimate) covered properly. Why sell outside the app stores Margin. You keep 85–100% instead of giving up the store's cut. Control. Your own pricing, trials, upgrades, and refund policy — no review gatekeeping, no waiting on approval to ship a fix. The relationship. You get the customer's email and can actually support and re-sell to them. The tradeoff is that the things the store did invisibly — vouching for your binary, taking payment, enforcing the purchase — are now your job. This isn't a Mac thing. Windows devs sell direct constantly, Linux too, and a Tauri or Electron app ships to all three from one codebase. The work below applies across the board. 1. Distribution and updates Before anyone pays, they have to trust and install the thing. macOS: sign with a Developer ID certificate and notarize with Apple, or Gatekeeper will scare users off. Windows: an Authenticode code-signing certificate, ideally EV to build SmartScreen reputation faster. Linux: package as AppImage, .deb / .rpm , or Flatpak depending on your audience. Then updates, because the store won't push them for you: Sparkle (macOS), Squirrel/electron-updater (Electron), the Tauri updater , or your own endpoint. Decide this early — retrofitting auto-update onto a shipped app is miserable. 2. Getting paid Two real models: Stripe (you're the merchant). Lower fees, full control, your brand on the receipt. The catch: sales tax and EU VAT are your responsibility (handle it yourself or bolt on a tax service). Merchant of Record (Lemon Sque
In my journey as a backend developer, I had already spent time working with APIs, databases, authentication flows, and background processing. I understood the basic idea that not everything should occur within a request-response cycle, especially when dealing with expensive operations such as sending emails, processing files, or generating reports. Offloading work to the background felt like a solved problem to me. That confidence was exactly what led me into confusion. When I first encountered message brokers and task queues, they looked like different names for the same idea. Both involved queues, both involved workers, and both involved asynchronous processing. In my head, the distinction didn’t seem important, so I treated them interchangeably and assumed that choosing one over the other was just a matter of preference or framework availability. The real issue was that I had not yet understood the difference in intent between communication and execution. What I thought was a simple design choice actually turned into an architectural mistake that affected how I structured an entire system. How I Misunderstood the Problem At the time, I was building systems where the backend had to handle multiple heavy operations. A user could upload files, request reports, or trigger processes that should not block the main API response. Naturally, I reached for a queue-based solution because it is the standard answer for background work. However, instead of asking what role the system needed to play, I focused on what tool could make things asynchronous. That small shift in thinking created the confusion. I assumed that anything that gets delayed or processed later should automatically go into a queue, without distinguishing whether I was dealing with a job that must be executed or an event that other services should react to. This is where I started building the wrong abstraction. Where Task Queues Actually Fit A task queue exists primarily to assign work that must be complete
estou tentando resolver algo q me incomoda no Clion com C++ que é o terminal dele, estou tentando adiconar um terminal proprio com SFML 3, minha ideia é escrever o codigo e ele ser compilado e assim aparecer na janela do SFML, alguem tem alguma ideia dew como posso fazer isso? submitted by /u/wzinho_667 [link] [留言]
submitted by /u/mitousa [link] [留言]
For a long time, I hesitated to share my work. I kept telling myself: "If I post a simple hero section, a basic Bootstrap grid, or a landing page clone, people will judge me. They’ll think I’m not a 'real' developer yet." But today, I saw a video of a developer who built a complete Netflix clone using only HTML & CSS in just 4 hours https://x.com/Aditwariii/status/1681403710457643009?s=20 . It made me stop and think. It’s easy to get so obsessed with complex frameworks, cloud architectures, and database optimizations that we begin to look down on the fundamentals. But here is the psychology of software engineering that we often ignore: Every master was once a beginner: The engineers managing complex distributed systems today started exactly where we are—struggling to center a div and fighting with CSS media queries. Shipping beats hiding: Building a clean, responsive interface in 4 hours shows speed, focus, and attention to detail. Those are core professional hygiene habits. Code is for humans, not just machines: Before we write APIs or database queries, we must master how a human being actually interacts with our interface. I’m letting go of the fear of being judged for "simple" things. From now on, I am building in public. Whether it’s a massive full-stack application or just a beautifully aligned hero section, it is proof of active practice and continuous momentum. Massive respect to [ https://x.com/Aditwariii?s=20 Check out Aditya Tiwari on X. POLYMATH 🧑💻 sde @IEX_INDIA_ ] for the inspiration and the reminder to keep shipping! 👇 What is a "small" project or layout you built recently that taught you a major lesson? Let's connect in the comments.
Java 26 was officially released in March 2026, and after spending the past three months exploring its new features, experimenting with preview APIs, and using it in personal projects, I think it's a good time to share my impressions. Unlike launch-day articles that simply list every new feature, this is a practical look at what actually stood out to me after having some time to work with Java 26. Some improvements are immediately useful, while others feel like building blocks for the future of the language. Java continues its predictable six-month release cycle, and Java 26 is another example of gradual, thoughtful evolution rather than dramatic change. In this article, I'll cover the features I found most interesting, what I like, what I probably won't use right away, and whether I think Java 26 is worth upgrading to. Why Upgrade to Java 26? Every Java release makes the platform: Faster More secure Easier to write Better for cloud applications Even if you don't immediately use every new feature, upgrading allows you to benefit from JVM optimizations and improved tooling. 1. Better Performance Java 26 continues improving the JVM with optimizations for: Faster startup Better garbage collection Reduced memory usage Improved JIT compilation Most applications will benefit automatically without changing a single line of code. 2. Improved Pattern Matching Pattern matching keeps becoming more powerful. Instead of writing: if ( obj instanceof String ) { String text = ( String ) obj ; System . out . println ( text . length ()); } You can simply write: if ( obj instanceof String text ) { System . out . println ( text . length ()); } Cleaner code with less casting. 3. Record Improvements Records remain one of Java's best additions for immutable data. public record User ( Long id , String name , String email ) {} Instead of writing dozens of lines containing: constructor getters equals() hashCode() toString() Java generates them automatically. 4. Better String Templates (Previe
[ Removed by Reddit on account of violating the content policy . ] submitted by /u/AnearlyApp [link] [留言]
Many AI projects today are presented as multi-agent systems. One agent investigates. Another agent analyzes risk. A third agent checks compliance. A fourth agent gives a recommendation. It sounds advanced. But in a bank, adding more agents does not automatically make a workflow safe. A bank cannot freeze a customer account, block a payment, file a regulatory report, or label a transaction as fraud simply because an AI system produced a confident answer. The real question is not: How many AI agents are involved? The real question is: Can the system show evidence, challenge its own conclusion, apply deterministic rules, and stop for human approval when the decision is high impact? That is the difference between an interesting multi-agent demo and an enterprise-ready AI workflow. A banking example: suspicious wire transfer Imagine a bank detects a wire transfer for $250,000. The payment is unusual because: The customer has never sent a transfer of this size. The destination account is in a new country. The transaction happens outside the customer’s normal business hours. The beneficiary was added only a few minutes before the transfer. The customer recently changed their phone number and email address. A simple AI chatbot might say: “This transaction looks suspicious. Consider blocking it.” That is not enough. A bank needs to know: Which transaction patterns triggered the concern? Is the customer actually violating a known risk threshold? Is there a sanctions or AML issue? Could this be a legitimate business payment? What policy applies? Should the payment be blocked, held, or released? Who is allowed to make that decision? Can the bank explain the decision later to auditors, compliance teams, and the customer? This is where structured multi-agent design matters. A better design: a banking fraud decision room Instead of letting one model make a decision, the bank can create a controlled workflow with specialized agents. Transaction Alert ↓ Fraud Detection Agent ↓ Custo
So far we've been working with Swift's built-in types — String, Int, Bool, Array. But what if you...
the insight that started this project hit me while i was finishing a bytecode-compiled language i'd written in C i'd spent months building a hand-written lexer, a single-pass Pratt compiler, a stack VM with 35 opcodes, and a mark-and-sweep garbage collector. and right near the end i had this realization: an LLM inference engine is the same problem. it's a graph-compile plus memory-plan plus kernel-schedule problem. i'd just built one so i decided to find out if that was actually true the project the result is ignis, a from-scratch LLM inference engine in Rust. i used it specifically to see how far the compiler analogy held up. the dependency count ended up at 2: memmap2 (to mmap the weight blob off disk) and fancy-regex (for one look-ahead in the BPE tokenizer). everything else is hand-written, because the whole point was to understand what's actually happening the compiler analogy holds up better than i expected the interesting part of any inference engine isn't loading the weights or doing matrix math. it's what happens between "here's a compute graph" and "here's an efficient execution plan." that's a compiler problem ignis builds an SSA (static single assignment) IR of the entire Qwen2 forward pass. every operation in the transformer (the RMSNorm layers, the SwiGLU activations, the attention projections, all of it) becomes a node in the graph with explicit data dependencies then fusion passes run over the graph. the intuition is simple: if operation B always and only reads the output of operation A, you can merge them into one op and eliminate the intermediate buffer. in practice this fused 49 RMSNorm ops and 24 SwiGLU ops, bringing the total from 435 operations down to 362 that part felt expected. the liveness analysis surprised me the liveness analysis after fusion, the graph still needs activation buffers: scratch memory to hold intermediate results as the plan executes. the naive approach allocates one buffer per node. the smarter approach asks: which buffer
submitted by /u/Best_Minimum4834 [link] [留言]
If you ever find yourself with time to kill and crave a fun challenge, you can write a program that prints out its own source code, called a quine ). Go on, give it a try, it's good fun! Once that's done, what's to stop you from modifying the source code instead of printing it verbatim, slowly shifting forms as you iterate on each successive output? Naturally, you'll want to make a game that's played in its own source code (click for an animation): #include<stdio.h> #define z else #define y return #define x int #define w if( #define v putchar( #define B v 10); #define A v 92); /* IOCCC29, w = up, e = down */ x a= 32 ; x b= 6 ; x c= -1 ; x d= 1 ; x e= 5 ; x f= 10 ; x g= 62 ; x h= 5 ; x i[6]={ 1,3,1,4,1,0} ; char*j[]={ "\ \ #include<stdio.h>'#define$z$else'#define$y$return'#define$x$int'#defin\ e$w$if('#define$v$putchar('#define$B$v$10);'#define$A$v$92);''/*$IOCCC\ 29,$w$=$up,$e$=$down$*/''x$a=","32",";x$b=","6",";x$c=","-1",";x$d=","\ 1",";x$e=","5",";x$f=","10",";x$g=","62",";x$h=","5",";x$i[6]={1,3,1,4\ ,1,0};char*j[]={","","};x$k=0;x$l=1;x$m(){l++;w$l==1)y!v$44);w$l==2)y!\ v$34) ;char$o=j[k][l-3];w!o){l=0;k++;y!v$34);}w$o==34){A$y$v $34);\ }w$o= =92){A$y$A}w$o!=32&&o!=1 0)y!v$o);y$m();}void$n(x$o, x$p){\ aspri ntf(j+o,\"%i\",p);}x$mai n(x$o,char**p){char*q;w$c<2 )a+=c\ ;b+=d ;x$r=b+2>f/2&&b<f/2+5;x$s=a+2==g&&b+2>h&&b<h+5;w$c<2){ w$a==\ e+2&& r||s){a-=c;b-=d;c=-c;}w$a<0||a>67){w$a<0){c=2;d=0;}a=3 4;b=6\ ;}w$b<0||b>13){b-=d;d=-d;}w$f/2>10)f-=2;w$h>10)h--;w$o>1){w*p[1]==119&\ &h>0)h--;w*p[1]==101&&h<10)h++;}s=f/2-b+1;w$s<0)f++;w$s>0)f--;}z{b++;w\ $d<0)d++;w$b>=13){w$o>1&&*p[1]==119)d=-4;b=13;}w$f/2<15-i[c-2])f+=2;z$\ e--;w$h<15-i[c-1])h++;z$g--;w$e+3<=0){c++;w$c<7){e=g;f=h*2;g=70;h=15-i\ [c-1];}z{e=5;g=62;c=1;d=1;}}w$a+2==e&&r||s){c=2;e=5;f=28;g=62;h=12;}}n\ \ (1,a);n(3,b);n(5,c);n(7,d);n(9,e);n(11,f);n(13,g);n(15,h);for(s=0;s<","29",";s++){w$s)v$32);q=j[s];r=1;for(char*t=q;*t;t++)w*t==","36",")v$32);z$w*t==","39",")B$z$w*t!=32&&*t!=10){r=0;v*t);w*t==123||*t==125||*t
submitted by /u/caspervonb [link] [留言]