Why I Handcrafted “Material Nova” — The Ultimate, Eye-Friendly VS Code Theme

Material Nova Logo

Let’s be honest. As software engineers, our code editor is where we spend 90% of our waking hours. We push pixels, debug tricky production errors, and write thousands of lines of code daily.

Yet, so many of us struggle with severe eye strain, visual fatigue, and headaches because our theme’s contrast is either too aggressive, or completely washed out.

After jumping between One Dark Pro, Community Material Theme, and Tokyo Night for years, I decided enough was enough. I needed a theme that didn’t just look stunning, but was also carefully calibrated for absolute developer comfort and accessibility.

So, I built Material Nova. 🎉

🎨 What is Material Nova?

Material Nova is a premium color theme for Visual Studio Code, meticulously engineered around Google’s legendary Material Design color palette.

Unlike other theme packages that only give you a single dark layout, Material Nova comes fully loaded with 12 distinct, premium variations designed to adapt to any lighting environment — from pitch-black late-night coding to bright sunny outdoor workspaces.

Material Nova Logo

🚀 The 12-Variant Palette Breakdown

Here is a quick look at the carefully engineered environments included in the package:

Theme Variant Background Ideal For
🌌 Default #263238 The timeless, classic Google Material design interface.
🖤 Darker #212121 Distraction-free, matte-gray elegance.
🔮 Palenight #292D3E A gorgeous synthwave, violet-blue cosmic space.
🌊 Ocean #0F111A A calming, deep-sea marine navy environment.
🌲 Deepforest #141F1D An organic green aesthetic that instantly relaxes the eyes.
☀️ Lighter #FAFAFA A crisp, ultra-clean light mode for bright, sunny office rooms.

Every dark theme variant includes a companion “High Contrast” (HC) edition — featuring pure pitch-black backgrounds and sharp, high-visibility borders for maximum legibility.

🔥 Why Make the Switch to Material Nova?

Here is what makes this theme package stand out from the crowd:

1. Zero Cognitive Fatigue (WCAG AAA Compliant)

We spent weeks tuning color pairings. The contrast ratios are strictly optimized to make variables, strings, brackets, and keywords immediately recognizable — without being overly bright.

2. Tailored Semantic Highlighting

Whether you are writing React (TSX/JSX), TypeScript, JavaScript, Python, C#, HTML, or CSS, the semantic syntax highlighting is incredibly precise. Different data types and syntax scopes have custom-assigned rules that standard themes simply group together.

3. Lightweight & Lightning Fast

No background scripts, no dynamic theme loading delays, and zero overhead. Material Nova consists of pure, hand-coded JSON declarations — which means your editor loads and responds instantaneously.

⚙️ How to Make it Look Premium

To get the absolute best visual experience out of Material Nova, open your settings.json in VS Code and paste this recommended configuration:

“`json title=”settings.json”
{
“editor.fontFamily”: “JetBrains Mono, Fira Code, monospace”,
“editor.fontSize”: 14,
“editor.lineHeight”: 1.6,
“editor.letterSpacing”: 0.5,
“editor.fontLigatures”: true,
“editor.cursorBlinking”: “smooth”,
“editor.cursorSmoothCaretAnimation”: “on”
}




<Callout type="info">
  For the best font rendering, install [JetBrains Mono](https://www.jetbrains.com/lp/mono/) — it's free, open-source, and designed specifically for developers.
</Callout>

---

## 📦 Installation

Install Material Nova directly from the VS Code Marketplace in seconds:



```bash
# Via VS Code Quick Open (Ctrl+P / Cmd+P)
ext install dhavalkurkutiya.vsc-material-nova

Or search “Material Nova” in the Extensions panel (Ctrl+Shift+X).

🔗 Links

OCI Full Stack Disaster Recovery (FSDR) Deep Dive: Architecture, Switchover, Failover, and Recovery Workflows

Introduction

Disaster recovery in cloud environments is no longer limited to restoring virtual machines or recovering storage volumes. Modern enterprise applications depend on tightly coupled compute, networking, databases, load balancers, DNS, and application dependencies.

OCI Full Stack Disaster Recovery (FSDR) introduces orchestration-driven recovery workflows that coordinate infrastructure and application recovery across regions while minimizing operational risk and downtime.

FSDR IS NOT BACKUP
Backup protects data.
Disaster recovery restores application continuity.

FSDR focuses on orchestrating complete application recovery, not only restoring individual resources.

This blog explains the deeper architecture and operational concepts behind OCI FSDR, including recovery orchestration, dependency sequencing, traffic redirection, resiliency engineering, and enterprise recovery design patterns.

Traditional backups help restore files or databases, but enterprise applications require coordinated recovery across multiple infrastructure layers.

Example:

Database restored successfully
→ application services unavailable
→ load balancer returns errors
→ business outage continues

Architecture Overview

FSDR setup follows a simple two-region design. The primary region hosts the live application stack, including compute, load balancer, database, and storage components. The secondary region keeps the standby resources ready for recovery.

All these resources are placed into Disaster Recovery Protection Groups, which help FSDR understand what belongs together. Once the groups are created, recovery plans can be built to define the exact order of actions during switchover or failover. This makes disaster recovery far more predictable and much easier to test.

Enterprise Multi-Region Disaster Recovery Architecture

Primary and DR Region Design

Users
│
▼
Primary OCI Region
│
├── Public Load Balancer
├── Web Tier
├── Application Tier
├── Database Tier
└── Storage Layer
│
Replication / Synchronization
│
▼
Disaster Recovery Region
│
├── Standby Infrastructure
├── Recovery Workflows
├── Replicated Data
└── Traffic Redirection

Understanding Recovery Orchestration

One of the most important concepts in FSDR is orchestration.

FSDR does not recover everything simultaneously.

Instead, recovery occurs in dependency-aware orchestration stages.

Example Recovery Workflow

  1. Validate DR environment
  2. Attach replicated storage
  3. Recover database services
  4. Validate database health
  5. Start application services
  6. Start web services
  7. Update load balancer routing
  8. Redirect traffic
  9. Validate application response

This sequencing reduces operational failures during recovery events.

Why Dependency Order Matters

Application continuity depends heavily on startup sequencing.

Incorrect startup order is one of the most common disaster recovery failures.

Example:

Web tier starts before database recovery
→ application connection failures
→ unstable service state

OCI FSDR helps coordinate these dependencies through orchestrated recovery execution.

Traffic Flow During Disaster Recovery

Understanding traffic movement during failover is critical.

Normal Traffic Flow
Users
│
▼
Primary Load Balancer
│
▼
Application Stack

Disaster Event
Primary region unavailable
Recovery Flow
FSDR initiates recovery workflows
→ DR region activated
→ services validated
→ traffic redirected
→ application restored

Switchover vs Failover

Although these terms are often used interchangeably, operationally they are very different.

Switchover

Switchover is a controlled transition between regions.

Controlled migration with synchronized application state.

Typical use cases:

✔ Planned maintenance
✔ DR drills
✔ Infrastructure migration
✔ Region transition testing

Failover

Failover occurs during an actual disruption.

Emergency recovery during infrastructure failure.

Typical use cases:

✔ Region outage
✔ Critical disaster
✔ Connectivity failure
✔ Infrastructure incident

Key Operational Insight

Switchover focuses on continuity.
Failover focuses on survivability.

Recovery Objectives in Enterprise DR

Disaster recovery design is heavily influenced by two key metrics.

RTO (Recovery Time Objective)
Maximum acceptable downtime.

Example:

Application must recover within 15 minutes.
RPO (Recovery Point Objective)
Maximum acceptable data loss window.

Example:

5-minute replication lag accepted.

Important Design Insight

Lower RTO and RPO increase infrastructure complexity and operational cost.

This is one of the biggest design tradeoffs in enterprise disaster recovery.

Observability During Disaster Recovery

Recovery orchestration without observability creates blind operational recovery.

Monitoring and validation are essential during DR events.

Critical observability areas include:

✔ Replication health
✔ Recovery progress
✔ Application validation
✔ Service health
✔ Traffic routing
✔ Error monitoring

Without proper validation, infrastructure may recover while applications remain unavailable.

Real Enterprise Scenario

Consider a multi-tier banking application deployed across OCI regions.

Architecture:

Internet
│
▼
Public Load Balancer
│
▼
Web Tier
│
▼
Application Tier
│
▼
Database Tier

Disaster Recovery Deployment Models

One of the most important architectural decisions in disaster recovery design is selecting the appropriate DR deployment model.

The choice depends on:

✔ Recovery speed requirements
✔ Business criticality
✔ Infrastructure cost
✔ Operational complexity
✔ Acceptable downtime
✔ Recovery objectives (RTO/RPO)

Enterprise DR strategies are commonly divided into:

✔ Cold DR
✔ Warm DR
✔ Hot DR

old Disaster Recovery (Cold DR)
What is Cold DR?

Cold DR is the most cost-optimized disaster recovery model.

Simple explanation:

Infrastructure is created only during disaster recovery events.

In this model, the DR region does not continuously run the full application stack.

Instead:

✔ Backups are stored
✔ Configurations are maintained
✔ Infrastructure is provisioned during disaster
Cold DR Architecture
Primary Region
│
├── Running Production Environment
│
▼
DR Region
│
├── Backup Storage
├── Infrastructure Templates
└── Minimal Active Resources

**Cold DR Workflow

During disaster:**

  1. Disaster detected
  2. Infrastructure provisioned in DR region
  3. Storage restored
  4. Database recovered
  5. Application deployed
  6. Traffic redirected

Warm Disaster Recovery (Warm DR)

What is Warm DR?

Warm DR provides a balance between recovery speed and infrastructure cost.

Simple explanation:

A partially running standby environment exists in the DR region.

Some infrastructure components remain active continuously.

Example:

✔ Database replication active
✔ Standby compute available
✔ Networking preconfigured
✔ Application services partially ready
Warm DR Architecture
Primary Region
│
├── Fully Active Environment
│
Replication
│
▼
DR Region
│
├── Standby Database
├── Preconfigured Networking
├── Minimal Compute
└── Recovery Automation

Warm DR Workflow

During disaster:

  1. DR database promoted
  2. Additional compute started
  3. Application services activated
  4. Load balancer updated
  5. Traffic redirected

Hot Disaster Recovery (Hot DR)

What is Hot DR?

Hot DR is the most advanced disaster recovery model.

Simple explanation:

A fully active standby environment continuously runs in the DR region.

Both regions remain operational simultaneously.

The DR region is always ready for immediate failover.

Hot DR Architecture
Primary Region
│
├── Active Production Stack
│
Real-Time Replication
│
▼
DR Region
│
├── Fully Active Standby Stack
├── Running Applications
├── Active Networking
└── Immediate Traffic Readiness

**Hot DR Workflow

During disaster:**

  1. Primary outage detected
  2. Traffic immediately redirected
  3. DR environment already operational
  4. Minimal recovery delay

During disaster:

Primary region unavailable
→ FSDR executes recovery orchestration
→ DR database activated
→ application services recovered
→ traffic redirected
→ banking services restored

Common Disaster Recovery Failures

Many DR failures occur during orchestration and validation rather than infrastructure provisioning.

Common issues include:

✔ Missing dependency mapping
✔ DNS still pointing to failed region
✔ Replication lag ignored
✔ Application validation skipped
✔ Untested DR workflows
✔ Incorrect startup sequencing

Critical Operational Insight
Most DR failures occur during orchestration and validation, not infrastructure provisioning.
Why OCI FSDR Matters

Cloud resiliency is no longer only an infrastructure recovery problem.

Modern disaster recovery is an application orchestration challenge.

OCI FSDR helps organizations move from:

Manual recovery
→
Automated resiliency engineering

through coordinated recovery workflows across regions.

Production Best Practices
✔ Perform regular DR drills
✔ Validate application dependencies
✔ Continuously monitor replication
✔ Test traffic failover procedures
✔ Maintain updated recovery documentation
✔ Validate application health after recovery
✔ Separate production and DR environments

Oracle FSDR official Doc :

Conclusion

OCI Full Stack Disaster Recovery enables organizations to orchestrate application-aware disaster recovery workflows across OCI regions.

By coordinating dependency sequencing, traffic routing, recovery validation, and service orchestration, FSDR helps reduce downtime and operational complexity during disaster events.

Modern disaster recovery is no longer just about recovering infrastructure — it is about restoring complete business continuity through intelligent orchestration and resiliency engineering.

Test Cost Reduction Playbook: AI-Powered Testing on a Shoestring Budget

Test Cost Reduction Playbook

AI-Powered Testing on a Shoestring Budget

Stop burning money on test automation. Start testing smarter.

1. Know Your Current Test Costs

Most teams don’t know what they’re actually spending on testing. Here’s a framework to calculate your real costs.

The Real Cost of Testing Worksheet

Category A: API & Infrastructure

Item Monthly Cost Notes
AI model API calls $_____ Check your usage dashboard
GPU / cloud instances $_____ For vision models or local LLMs
CI runner minutes $_____ GitHub Actions, Jenkins, etc.
Domain & hosting $_____ For test management tools
Subtotal $_____

Category B: Human Time

Activity Hours/Month Hourly Rate Cost
Writing test scripts _____ $_____ $_____
Debugging flaky tests _____ $_____ $_____
Test data setup _____ $_____ $_____
Reviewing results _____ $_____ $_____
Subtotal _____ $_____

Category C: Context Switching & Waste

  • Tools purchased but never used: $_____
  • Failed test runs that needed re-execution: $_____
  • Time spent fighting brittle selectors: $_____

The Rule of Thumb

If your AI testing API bill exceeds $50/month for a solo tester, you’re overpaying.

If your team spends more than 30% of testing time on maintenance (not new tests), you have a cost problem.

2. Three Most Expensive Mistakes

Mistake #1: Vision Models for Everything

The trap: Every AI testing tutorial pushes multi-modal vision models. Screenshot → AI analyzes → click. It feels magical.

The real cost:

  • Qwen-VL-Plus: ~$0.011/step, 50 steps = $0.55
  • GPT-4o vision: ~$0.015/step, 50 steps = $0.75
  • Claude 3.5 Sonnet vision: ~$0.012/step, 50 steps = $0.60

The fix: Ask yourself: Does this test actually need to SEE the page?

90% of web testing is CRUD operations — filling forms, clicking buttons, reading text. The DOM already has all that information as structured text. Vision is only needed for:

  • Visual regression (did the layout break?)
  • CAPTCHAs
  • Canvas / SVG-heavy apps

For everything else, text-based approaches cost 200-300x less.

Mistake #2: Self-Hosting GPU Instances

The trap: “I’ll run a local LLM — no API costs!”

The real cost:

  • NVIDIA A100 cloud instance: ~$3,000/month
  • RTX 4090 (one-time): ~$1,600 + electricity
  • Setup time: 2-5 days
  • Maintenance: ongoing

The fix: Use API-based models for development, switch to local only if you have very high volume (>100k requests/month) and engineering time to manage it.

For reference: DeepSeek V4 Flash API costs $0.14/M input tokens. A typical test step uses ~2000 tokens ≈ $0.00035. You’d need to run 300,000+ test steps per month to justify a GPU.

Mistake #3: Over-Automating Everything

The trap: “We need 100% automation coverage!”

The real cost:

  • Each automated test requires 2-5x more maintenance than its manual equivalent
  • Flaky tests waste debugging time
  • 20% of tests catch 80% of bugs

The fix: The 80/20 rule:

  • Automate the happy path and critical flows
  • Keep edge cases manual
  • Review automation ROI quarterly

A focused suite of 20 well-maintained tests beats 200 flaky ones every time.

3. The Text-Only DOM Approach

This is the core technique that cut my costs by 300x. It works for any web application.

How It Works

Task: "Login system, search product, add to cart"
         ↓
① Extract interactive elements from DOM tree
   (No screenshots. Pure text. Zero image tokens.)
         ↓
② LLM analyzes structure + decides next action
   (~2000 tokens/step ≈ $0.00035)
         ↓
③ Execute action (Playwright click / fill / select)
         ↓
④ Back to ① until task completes

What the AI Actually Sees

Instead of a screenshot:

URL: https://example.com/login
Title: Login Page
Interactive elements: 12

[0] <input placeholder="Email" name="email">
[1] <input placeholder="Password" type="password">
[2] <button>Sign In</button>
[3] <a>Forgot password?</a>
[4] <a>Register</a>
...

That’s it. Clean, structured, cheap. No base64 image data, no rendering overhead.

Cost Comparison

Approach Per Step 50-Step Test 1000 Tests/Month
Vision model (Qwen-VL) ~$0.011 ~$0.55 ~$550
Vision model (GPT-4o) ~$0.015 ~$0.75 ~$750
Claude Sonnet vision ~$0.012 ~$0.60 ~$600
DOM + DeepSeek V4 Flash ~$0.00035 ~$0.018 ~$18
DOM + GPT-4o mini ~$0.00015 ~$0.0075 ~$7.50

Implementation in 10 Lines

// The core loop: extract -> decide -> act -> repeat
const extractDOM = async (page) => {
  return page.evaluate(() => {
    const elements = document.querySelectorAll(
      'button, a, input, select, textarea, [role="button"], [tabindex]'
    );
    return [...elements]
      .filter(el => el.offsetParent !== null)
      .map((el, i) => `[${i}] <${el.tagName.toLowerCase()}>${el.textContent.trim() ? ' "' + el.textContent.trim() + '"' : ''}${el.placeholder ? ' placeholder="' + el.placeholder + '"' : ''}`)
      .join('n');
  });
};

No API call for vision. No screenshots. Just structured text.

When This Approach Fails

  • Canvas-rendered apps (Figma, games): Need vision
  • Highly dynamic SPAs with shadow DOM: Need custom element extraction
  • Visual assertions (the blue button should be red): Need screenshots

For everything else — login, forms, navigation, CRUD — text-only wins on cost, speed, and reliability.

4. Mobile Testing on a Budget

Mobile testing doesn’t have to mean expensive device farms and premium cloud services.

The Budget Mobile Stack

Component Budget Option Cost
Device Android emulator (MuMu, BlueStacks) Free
UI extraction uiautomator2 Free
Text input ADB shell input + send_keys Free
OCR EasyOCR (local, no API) Free
Decision engine DeepSeek V4 API ~$0.00035/step
Physical device Old Android phone on USB $0-50

Total setup cost: $0 (if you already have a computer)

The Hybrid Approach

Android apps can’t give you a clean DOM tree like web pages. But they give you something close enough:

  1. Use uiautomator2 to extract the native UI hierarchy (text-based, just like DOM)
  2. Fall back to ADB screencap + local OCR only when UI tree is empty (e.g., WebView pages)
  3. Same decision engine — just different input sources

The WebView Input Hack

Hybrid apps (Uni-app, React Native WebView, Flutter WebView) won’t respond to standard set_text(). The fix:

# Python + uiautomator2 for hybrid app inputs
import uiautomator2 as u2
d = u2.connect()
input_field = d(text="Type a message")
input_field.click()
import time; time.sleep(0.5)
# Use send_keys, NOT set_text - critical difference
d.send_keys("Hello from automated test", clear=True)
# Click send button
d.click(1260, 2470)

send_keys() sends characters through the IME (input method editor), which works where set_text() fails because it bypasses the app’s event handling.

5. When You SHOULD Spend Money

Cost reduction doesn’t mean zero spending. Here’s where money is well spent.

Worth Every Penny

Spend Why Monthly Budget
Good API model (DeepSeek V4 / GPT-4o mini) Cheaper than your time debugging bad decisions $5-20
Playwright Free, open source, no-brainer $0
CI minutes (GitHub Actions) Free tier covers small teams $0
Local OCR (EasyOCR, PaddleOCR) One-time setup, zero API cost $0

Nice to Have (when budget allows)

Spend Why Monthly Budget
Visual regression tool (Percy, Applitools) Catches layout bugs $50-200
Device cloud (BrowserStack, SauceLabs) Physical device coverage $50-200
Test management tool (TestRail, qTest) Reporting for stakeholders $25-50

Never Spend On

  • ❌ GPU instances for solo testing (use APIs instead)
  • ❌ Multiple AI subscriptions you barely use
  • ❌ Over-engineered test frameworks

6. Tool Comparison & Cost Matrix

AI Models for Testing

Model Cost/M Input Cost/M Output ~Cost/Step Best For
DeepSeek V4 Flash $0.14 $0.28 ~$0.00035 DOM-based decisions
GPT-4o mini $0.15 $0.60 ~$0.00015 DOM + some reasoning
Gemini 2.0 Flash $0.10 $0.40 ~$0.0001 Budget alternative
Claude 3 Haiku $0.25 $1.25 ~$0.0003 Fast, reliable
Qwen-VL-Plus $0.08/img $0.08 ~$0.08 Visual testing
GPT-4o $2.50 $10.00 ~$0.015 Complex visual analysis

Test Automation Frameworks

Framework Cost AI-Native Cross-Platform Learning Curve
Playwright Free No Web Medium
uiautomator2 Free No Android Low
Midscene.js Free Yes Web Medium
browser-use Free Yes Web High

The Optimal Budget Stack (Solo Tester)

Category Tool Cost
Web automation Playwright Free
Android automation uiautomator2 Free
AI decision engine DeepSeek V4 Flash ~$5-10/month
Local OCR EasyOCR Free
CI/CD GitHub Actions Free
Version control GitHub Free
Total $5-15/month

7. The Solo Tester Cost-Cutting Checklist

Setup Phase

  • [ ] Audit current API spending — check last 3 months
  • [ ] Cancel unused subscriptions (be ruthless)
  • [ ] Set up cost alerts on all API dashboards
  • [ ] Install local OCR (EasyOCR / PaddleOCR — free)
  • [ ] Choose one primary LLM for test decisions

Monthly Review

  • [ ] Review test suite: remove tests that haven’t caught bugs in 3 months
  • [ ] Check API bill: is it under $20?
  • [ ] Audit flaky tests: are >10% flaky? Fix or remove
  • [ ] Visual model usage: did you really need it?
  • [ ] CI minutes: are you paying for wasted runs?

Quarterly

  • [ ] Re-evaluate tool subscriptions
  • [ ] Compare current LLM pricing (models drop prices fast)
  • [ ] Review automation ROI: time saved vs. time spent
  • [ ] Update test suite: add new critical paths, remove stale ones

Red Flags

  • [ ] API bill > $50/month for a solo tester
  • [ ] Test maintenance > 30% of testing time
  • [ ] Running vision models on DOM-interactable pages
  • [ ] Self-hosting GPU for testing
  • [ ] >5 test automation tools installed but only 2 used regularly

Appendix: Quick Starts

A. DeepSeek V4 Setup (5 minutes)

# 1. Get API key from platform.deepseek.com
# 2. Set environment variable
export DEEPSEEK_API_KEY=sk-your-key-here

# 3. Test the API
curl https://api.deepseek.com/v1/chat/completions 
  -H "Content-Type: application/json" 
  -H "Authorization: Bearer $DEEPSEEK_API_KEY" 
  -d '{
    "model": "deepseek-chat",
    "messages": [{"role": "user", "content": "Extract interactive elements from this page: [paste DOM here]"}]
  }'

B. Playwright DOM Extraction (2 minutes)

const { chromium } = require('playwright');
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://your-test-url.com');

const dom = await page.evaluate(() => {
  const els = document.querySelectorAll('button, a, input, select, textarea');
  return [...els]
    .filter(el => el.offsetParent !== null)
    .map((el, i) => `[${i}] ${el.tagName} "${el.textContent.trim()}"`)
    .join('n');
});
console.log(dom);

C. uiautomator2 + ADB (3 minutes)

# Install
pip install uiautomator2

# Connect device
python -m uiautomator2 init

# Quick test script
python -c "
import uiautomator2 as u2
d = u2.connect()
print(d.info)
ui = d.dump_hierarchy()
print(ui[:500])
"

This playbook was built from real production experience — running AI-powered testing on web and Android apps across healthcare, fintech, and e-commerce projects. Every cost figure comes from actual API bills, not theoretical estimates.

15 years in software testing, from manual testing to AI-driven automation. Currently building cost-effective testing solutions for solo engineers and small teams.

More practical testing prompts and techniques:
👉 xulingfeng.gumroad.com/l/vkhhq

Zero-Account File Sharing: The Tool That Gets Out of Your Way

1. The Developer’s Daily Friction: Why Account-Based File Sharing Is Often a Pain

As developers, our days are filled with creating, debugging, and collaborating. We constantly need to share files: config snippets, log files, quick .gif demos, mockups, or even small asset packs. But how often do we hit a wall with the very tools meant to help us share? It’s a common scenario:

  • “Can you send me that .json file?”
  • “Sure, I’ll just upload it to [Cloud Storage X].”
  • Five minutes later: “Wait, you need to log in to access it.” or “The permissions are all wrong.”

This isn’t just an inconvenience — it’s a productivity killer. Every extra click, every account creation prompt, every login screen, every “change permission” step breaks our flow. It forces a context switch from problem-solving to administration.

When I’m deep in a coding session, the last thing I want is to wrestle with an overly complex file-sharing service just to send a 5MB screenshot. Traditional file sharing, with its emphasis on permanent storage and user accounts, often introduces unnecessary friction for ephemeral, quick shares. Email attachments are often too small, and other services demand too much upfront commitment.

2. Simplicity as a Feature: Getting Out of the User’s Way

True elegance in a tool often lies in its ability to fade into the background. For file sharing, this means a singular focus: transferring a file from point A to point B with the least possible effort.

This is where the zero-account philosophy truly shines. Imagine this workflow:

  1. You have a file you need to send.
  2. You open a simple web interface and drag and drop the file.
  3. A link is generated instantly.
  4. You paste the link to your colleague.
  5. Done.

No accounts. No passwords. No “verify your email”. No “upgrade to pro”.

This isn’t about storing your life’s work — it’s about facilitating quick, temporary exchanges that keep your workflow smooth. For developers, this often means sharing small-to-medium sized files (up to ~100MB for direct transfer). Have something larger? Compress it into a .zip or .tar.gz and you’re good to go.

It’s about respecting your time and cognitive load. The less mental energy spent on the sharing mechanism, the more you have for the actual work.

3. The Power of Ephemerality and Privacy in Quick Transfers

Beyond speed, zero-account file sharing offers two underrated advantages: controlled ephemerality and end-to-end encryption.

Controlled ephemerality

Many tools in this space provide time-limited or download-limited links. This is a huge win for data hygiene, especially in development contexts. Consider:

  • Temporary API keys for testing — you don’t want these living forever in someone’s cloud drive.
  • Log snippets with sensitive information — share for debugging, but ensure they don’t persist indefinitely.
  • Early-stage mockups — share for quick feedback, knowing the link will expire, preventing stale versions from floating around.

When a link expires after a set time or number of downloads, you’re not creating a permanent digital footprint for every file you share.

End-to-end encryption

For sensitive files, E2EE ensures that only the intended recipient can access the content — not the server, not a third party. This matters when you’re sharing internal configs, client data snippets, or anything you wouldn’t want intercepted in transit.

Combined with zero-account access, this significantly reduces the attack surface. There’s no persistent user profile to compromise, and no lingering storage to breach. It’s not a complete security solution, but for specific use cases, it minimizes risk remarkably well.

4. Conclusion: Reclaiming Your Workflow with Unobtrusive Tools

In our increasingly complex digital lives, the tools that truly empower us are often the ones that demand the least attention. Zero-account file sharing embodies this principle — prioritizing speed, simplicity, and privacy for quick transfers.

For developers, reclaiming even a few minutes per day spent wrestling with logins and permissions adds up fast. That time goes back into what actually matters: building things.

I built SimpleDrop out of this exact frustration — a no-account, end-to-end encrypted file sharing tool that handles up to 100MB (compress for more), designed to stay completely out of your way.

What are your go-to tools for quick, hassle-free file sharing? And what’s the most annoying friction point you’ve hit with existing solutions? 👇

Running On-Device AI in a React Native App: Real-Time Hazard Detection with CoreML

I’ve been building a field inspection app where the core differentiator is this: AI that works with zero internet. No cloud call, no latency, no “sorry, you’re in a dead zone.” The model runs on the device and that’s the whole point.

This post is about shipping real-time on-device inference in a React Native (Expo) app — what the stack looks like, what actually tripped me up, and what the numbers look like so far.

The Setup

Safety inspection tool for construction sites. Inspectors walk a site, capture photos, and the AI flags PPE violations — missing hard hats, high-vis, etc. Construction sites often have zero connectivity. The AI has to work offline or it’s useless.

Stack:

  • React Native / Expo SDK 52
  • CoreML for inference (iOS)
  • YOLOv8s converted to .mlpackage — under 50MB, bundled with the app
  • Swift Expo module wrapping the CoreML inference pipeline

The model is under 50MB — you can’t ship a 400MB model and expect App Store approval or a sane user experience.

The Swift Module Bridge

The tricky part isn’t CoreML inference itself — Apple’s API is clean. The tricky part is bridging it into React Native without losing your mind.

I built a native Swift Expo module (PPEDetectorModule) that:

  1. Loads the .mlpackage on init
  2. Accepts a photo URI from JS
  3. Runs synchronous inference and returns bounding boxes + confidence scores
  4. Handles model load failures gracefully (falls back to “manual review required”)
func detect(imageUri: String) -> [[String: Any]] {
    guard let model = detector,
          let image = CIImage(contentsOf: URL(string: imageUri)!) else {
        return []
    }
    let results = try? model.predict(image: image)
    return results?.map { box in
        ["label": box.label, "confidence": box.confidence,
         "x": box.rect.origin.x, "y": box.rect.origin.y,
         "width": box.rect.width, "height": box.rect.height]
    } ?? []
}

Results come back as plain JSON. The JS layer renders bounding boxes as an overlay using React Native’s Animated + absolute positioning.

Real-Time Viewfinder Mode

Post-capture detection is useful but not magical. For the real “wow” moment I wanted live inference as the inspector points the camera — hazards flagged before the photo is even taken.

The camera screen runs inference every 750ms against the live feed. At 750ms you get ~1.3fps of AI updates — visually responsive without hammering the CPU.

useEffect(() => {
  const interval = setInterval(async () => {
    if (cameraRef.current && isDetecting) {
      const frame = await cameraRef.current.takePictureAsync({
        quality: 0.3, skipProcessing: true
      });
      const detections = await PPEDetectorModule.detect(frame.uri);
      setBoxes(detections);
    }
  }, 750);
  return () => clearInterval(interval);
}, [isDetecting]);

Quality 0.3 is intentional — inference accuracy doesn’t need 12MP photos, and lower resolution dramatically cuts preprocessing time.

The Numbers (Sprint 2, iPhone 14 Pro)

  • Inference time (post-capture): ~280ms average
  • Viewfinder inference: ~320ms including frame capture overhead
  • Target SLA: <500ms — currently green ✅
  • Memory footprint: ~180MB at peak (well under 512MB budget)
  • Battery: Sprint 2 goal is <5% per shift — not fully measured yet

Model accuracy is the real open question. In simulator testing against construction site photos, hard-hat detection is solid. Field testing on actual sites is the Sprint 2 gate — target is 90%+ detection on hard-hat violations in real conditions.

Freemium Gate

Free tier gets 10 AI detections/month. This can’t be bolted on after the fact — it has to be woven into the detection pipeline.

Every call to detectAndSave checks an entitlement store before firing inference. If the quota is hit, it emits a paywall event and the UI surfaces an upgrade prompt. RevenueCat handles the StoreKit 2 subscription state.

Lesson: build the gate into the data layer, not the UI layer. If you gate at the UI, someone will bypass it. Gate at the function that writes to your database.

What’s Next

Tap-to-confirm UX for flagged hazards, App Store Connect subscription products, and the actual field test. Sprint 2 demo is at day 60 — success criteria is 90%+ hard-hat violation detection offline.

On-device AI is genuinely viable for production mobile apps right now. Models are small enough, hardware is fast enough, and the offline story is compelling in markets where connectivity is unreliable. If you’re building in field service, construction, agriculture, or any domain where “no signal” is a real scenario — worth considering CoreML + a bundled model over a cloud API dependency.

AI agents are becoming the new IDE. Keep your hands on the wheel.

Something shifted this week. AI coding tools are no longer being pitched as smarter autocomplete. They are being pitched as places where work happens.

Google used I/O 2026 to talk about an agentic developer future and an expanded Antigravity platform. OpenAI and Dell announced work to bring Codex into hybrid and on-prem enterprise environments. Reports also surfaced about Codex becoming available through ChatGPT on mobile. Different companies, different packaging, same direction: the coding assistant is moving out of the sidebar and into the center of the workflow.

I get why developers are tempted. A good agent can chase a bug, write the boring test scaffolding, inspect logs, open a pull request, and keep going while you answer email. That feels magical the first time it works.

It also feels dangerous when it almost works.

The IDE is turning into a delegation layer

For years, the IDE was where you typed code. Then it became where you searched docs, ran tests, reviewed diffs, managed containers, and talked to Copilot or ChatGPT. Now the next step is delegation: describe the task, hand over a repo, and let the agent make a plan.

That changes the developer’s job in a subtle way. You are still responsible for the code, but you may not be the person who wrote the first draft. You become the reviewer, debugger, architect, and adult in the room.

That can be a great trade. I would happily let an agent rename a field across a project, add a simple integration test, or draft a migration I already understand. I do not want it quietly deciding the shape of my auth system because my prompt was vague and I was tired.

Enterprise adoption will make this normal fast

The OpenAI and Dell announcement matters because it points at a boring but important problem: many companies cannot just throw private code into a public cloud workflow and hope legal approves it. Hybrid and on-prem setups are how AI coding agents move from demos to corporate defaults.

Once that happens, the social pressure changes. Today, using agents can still feel optional. Soon it may be built into the company toolchain, measured in dashboards, and assumed in sprint planning. That is where teams need better habits, not louder hype.

The question is not whether an agent can write code. It can. The question is whether your team can tell when the code is wrong.

Speed without review is just faster guessing

AI agents are excellent at producing plausible work. That is both the value and the trap. A diff can look clean. The tests can be green. The commit message can sound confident. And the behavior can still be off by one assumption that nobody checked.

If you want agents in your workflow, tighten the boring parts first. Keep tests close to the code. Make pull requests smaller. Write issue descriptions that include the actual acceptance criteria. Log the decisions that matter. Treat generated code like junior developer code from someone who never gets embarrassed and never admits uncertainty unless you force it.

That sounds harsh, but it is also freeing. You do not need to fear the tool. You need to manage it.

What I would do this week

If your team is experimenting with coding agents, pick one narrow task class and measure it honestly. For example: flaky test triage, dependency upgrades, documentation fixes, or small UI refactors. Do not start with architecture. Do not start with payments. Do not start with security-sensitive flows unless you already have strong review gates.

Also decide what the agent is not allowed to touch. That boundary matters. A tool that can edit anything will eventually edit something you wish it had not.

The developer who wins in this next phase will not be the one who blindly accepts every generated patch. It will be the one who can delegate clearly, review ruthlessly, and still understand the system when the agent gets lost.

That is the part I keep coming back to. AI can take over more of the typing. It cannot take responsibility for the product. That still belongs to us.

Source signals checked on May 20, 2026: Google News results from the last five days showed Google I/O 2026 developer coverage around Antigravity and agentic AI, OpenAI and Dell’s Codex enterprise announcement, and reports of Codex availability on mobile through ChatGPT.

Originally published at https://blog.jenuel.dev/blog/ai-agents-new-ide-hands-on-wheel

I deployed 12 vibe-coded apps to production. The same 6 things broke every single time.

Disclosure: I am a senior backend tech lead in Paris and I run HostingGuru, a small European PaaS. This article mentions HostingGuru once near the end. The content works on any platform you choose.

Last month, a non-tech founder messaged me at 23h on a Tuesday. Her Lovable-built waitlist had hit Product Hunt at noon Pacific, racked up 4,000 signups by dinner, and the dashboard was now returning a blank white screen. She had pushed nothing. Nothing changed. She also could not log in to her own admin panel.

I have seen this exact failure mode roughly 12 times in the last 8 months. Different founder, different tool (sometimes Lovable, sometimes Bolt, sometimes Cursor, sometimes Claude Code), same six holes. The code the model wrote was fine. Production exposed assumptions the model could not see.

This is a field guide to those six holes. If you have shipped (or are about to ship) anything you built with an AI coding tool, you will recognize at least three of them.

Why vibe-coded apps fail in production specifically

A coding model writes the code in front of it. It does not see your DNS, your env vars, your cold-start behavior, your database connection limits, your timezone, or the fact that your “production” environment is actually your laptop with ngrok pointed at it.

Models are also trained on a corpus where “it works locally” usually means “it works.” For a side project on a single laptop, that is true. For a deployed app with real users on real networks across real timezones, it is wildly false. The model is not lying. It just has no production telemetry. You have to add that yourself.

Here are the six patterns. Each one I have personally pulled out of a vibe-coded codebase in the last year.

Pattern 1: The API key that shipped in the JS bundle

Roughly 7 out of 12 vibe-coded apps I audited had at least one API key directly in client-side code. The model put it there because the model was told “call the Stripe API from this React form.” The model did not say “by the way, this key is now visible to every browser on Earth via View Source.”

The tell: you grep the dist/ or .next/ build output for the first few characters of your key, and you find it.

# Run this against your built output before deploying
cd dist && grep -r "sk_live_" . || echo "Clean"
cd .next && grep -r "sk_live_" . || echo "Clean"

Fix: every secret call goes through a backend route. The client sends a request to your server. Your server, holding the secret in an env var, calls the third party. Even for a static site, you can run a tiny serverless function or a single Express route for this. Costs nothing, takes 15 minutes.

If you only learn one thing from this post, learn this one. Leaked Stripe keys, leaked OpenAI keys, and leaked Resend keys cost real money and real customer trust. I have seen one founder rack up $1,800 in OpenAI charges in 11 hours because a leaked key got scraped off a GitHub mirror.

Pattern 2: The unbounded query that nukes the database

The model writes SELECT * FROM users WHERE org_id = ? and it works fine because in development you have 4 users. The same query in production, when your largest customer has 80,000 users, returns 80,000 rows over the wire, into your Node process, into JSON, and onto the network. Your API server’s memory spikes. Your database connection pool gets exhausted. Your dashboard returns the blank white screen.

This is the most common Lovable / Bolt failure I see, by a wide margin.

The fix is paginate everything and put an upper bound on every query. Pick a default limit (50 is fine for most things), enforce it at the query layer, and only allow the client to ask for more in explicit chunks.

// Bad (what the model wrote)
const users = await db.query('SELECT * FROM users WHERE org_id = $1', [orgId]);

// Better
const limit = Math.min(parseInt(req.query.limit) || 50, 200);
const offset = parseInt(req.query.offset) || 0;
const users = await db.query(
  'SELECT * FROM users WHERE org_id = $1 ORDER BY id LIMIT $2 OFFSET $3',
  [orgId, limit, offset]
);

While you are in there, add an index on the column you are filtering by. Most vibe-coded apps have zero indexes outside the ones the ORM created automatically on primary keys.

Pattern 3: The CORS wildcard that opens the front door

Every model I have worked with, when asked “I am getting a CORS error,” will reliably suggest setting Access-Control-Allow-Origin: *. It removes the error. It also opens your API to every site on the public internet, which is a problem the moment you have a session cookie or an auth token.

The right answer is to whitelist your own domain (and your staging domain, and localhost:3000 for dev).

// Express
const allowedOrigins = [
  'https://yourapp.com',
  'https://staging.yourapp.com',
  'http://localhost:3000',
];

app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
}));

If your AI tool wrote app.use(cors()) with no options, you have a wildcard. Audit it.

Pattern 4: The timezone mismatch nobody notices until billing

Your server is on UTC. Your dev laptop is on Paris time, or PST, or Bangalore. Your users are everywhere. The model probably wrote new Date() in a dozen places and the dates look “correct” to you because you are the only person who has looked at them.

Then a customer in Tokyo says “your invoice has the wrong month” and you discover that your billing cron, which runs at “midnight” your time, actually fires on the wrong day for half your customer base.

Three rules that catch most of these:

Store everything in UTC in the database. Always. ISO 8601, with the Z. If your ORM is doing anything else, fight it.

Display in the user’s timezone, computed on the client side or with the user’s stored preference. Never assume server-local.

For scheduled jobs (billing runs, daily digests, weekly emails), use cron-style scheduling that is explicit about timezone, or schedule them in UTC and document the offset.

// Use Intl on the client for display
const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const formatted = new Intl.DateTimeFormat('en-US', {
  timeZone: userTz,
  dateStyle: 'medium',
  timeStyle: 'short',
}).format(new Date(isoStringFromAPI));

This bug compounds with Pattern 2. If you have an unbounded query plus a bad timezone, you can ship invoices in the wrong currency to the wrong country with no audit trail. I have seen it.

Pattern 5: The file path that works on your laptop and nowhere else

fs.readFileSync('./data/seed.json') works perfectly when you run npm start from your project root. It explodes when your deploy runs from a different working directory, or when the file got excluded by your .gitignore, or when your build step did not copy it into the production bundle.

Same pattern: import config from '../../config/local.json'. The relative path resolves in development. In production, after the bundler did its thing, the path no longer exists.

The fix is to treat config as environment variables (the 12-factor way) and to load runtime data from a real data store, not from disk.

// Bad
const config = require('./config/local.json');

// Better
const config = {
  apiUrl: process.env.API_URL,
  dbUrl: process.env.DATABASE_URL,
  stripeKey: process.env.STRIPE_SECRET_KEY,
};

// Validate at boot so you fail loudly, not silently
const required = ['API_URL', 'DATABASE_URL', 'STRIPE_SECRET_KEY'];
const missing = required.filter(k => !process.env[k]);
if (missing.length) {
  console.error('Missing env vars:', missing.join(', '));
  process.exit(1);
}

That process.exit(1) is doing a lot of work. It is the difference between “the app crashes loudly at boot with a clear error” and “the app appears to boot fine but then 500s on the first request that needs Stripe.”

Pattern 6: The background work that never runs

Sending an email after signup. Charging a customer’s card on the 1st of each month. Cleaning up expired sessions. The model wrote these as inline calls inside your HTTP handler, or as setTimeout calls that get dropped when the process restarts.

The first time you redeploy, the queued work disappears. The first time your server cold-starts, the in-memory timers vanish. The first time an email fails to send (because Mailgun returned a 429), your signup handler returns 500 and the user thinks the app is broken.

You need three things: a real background process (a worker), a real schedule (cron, not setTimeout), and retry logic with a dead-letter behavior.

Lightweight options that have worked for me on small teams: BullMQ on top of Redis, RQ for Python, Sidekiq for Rails, GoCron for Go. Or use your platform’s managed worker tier if it has one.

The pattern in code:

// In the HTTP handler, do NOT send the email inline
await db.users.create({ email, hashedPassword });
await emailQueue.add('send-welcome', { userId: newUser.id });
return res.json({ ok: true });

// In the worker process
emailQueue.process('send-welcome', async (job) => {
  const user = await db.users.findById(job.data.userId);
  await mailer.send({
    to: user.email,
    template: 'welcome',
  });
  // BullMQ retries automatically with exponential backoff
});

If something fails repeatedly, it goes to the dead-letter queue and you see it in a dashboard. The user’s signup never broke. The email gets retried until it succeeds, or you decide it is hopeless.

What I built, because I kept solving these manually

After roughly the eighth time a non-tech founder messaged me at midnight with a vibe-coded app on fire, I built HostingGuru. It is a small European PaaS that runs your app from a GitHub repo, gives you a worker process if you need one, and watches your logs with a pattern-detection layer that catches the failure modes above (retry loops, hot Sentry fingerprints, latency spikes, silent cron failures) and pings me on Telegram. Free tier never sleeps, EU and US regions, encrypted env vars by default.

I am not telling you to use it. The patterns above work on Render, Railway, Fly, Vercel, AWS, or a Hetzner box you set up yourself. The point is: pick a platform that lets you have a worker process, real env vars, and log access. If your current setup does not, fix that before anything else.

What to do tonight, regardless of which platform you use

If you have a vibe-coded app in production right now, run this sweep. 30 minutes, maybe 45.

  1. grep -r "sk_live_|api_key|secret" dist/ .next/ public/ build/ against your built output. If anything matches, move the call to a backend route before you sleep.

  2. Open your three most-used database queries. Check for LIMIT. If it is missing, add it and a default of 50.

  3. Search your codebase for cors() with no arguments, or Access-Control-Allow-Origin: *. Replace with a whitelist of the 2 or 3 origins you actually need.

  4. Pick one date-handling code path (signup, billing, scheduled email). Verify it stores UTC and displays in user-local. If you cannot tell, log the Z suffix on the stored string and the Intl.DateTimeFormat output on the client.

  5. List your env vars. For each one, ask: would my app boot, or would it boot and then 500 on the first relevant request? Add a boot-time validator that crashes the process loudly if anything is missing.

  6. Find the one most important background job (welcome email, daily report, billing run). Verify it runs in a worker process or scheduled cron, not inside an HTTP handler.

  7. Set up one alert. Telegram, email, Slack, anything. The trigger does not need to be smart. “Error rate above 1% for 5 minutes” or “background job failed 3 times in a row” is enough to catch 80% of the bad nights.

The honest closing

Vibe-coded apps are not bad. They get founders shipping who otherwise could not ship, and that is wonderful. But the production gap is real, and every model I have used (Claude included) will confidently generate code that hides the six holes above. The model is doing what it was asked. You have to ask the next question.

I am curious: which of these six bit you the hardest? Or did you hit one I did not list? I have a strong suspicion there is a seventh I have not seen yet, and the comments here have been a good source of bug reports in the past.

Previous posts in this HostingGuru series:

  1. Heroku just went into “sustaining engineering mode.” Here are 5 alternatives whose free tier actually doesn’t sleep.
  2. I built my MVP with Claude Code. Now I need to deploy it. Here’s what nobody tells you.
  3. Your AI app is silently burning $2,000/month and you don’t know it. Here are the 5 patterns that bite founders.
  4. Telegram alerts for any production app, a 5-minute setup (no SaaS, no signup, just curl)
  5. How I built a Discord ‘ship-tracker’ bot in a weekend (and the 3-process architecture that keeps it alive 24/7)
  6. I migrated 12 client projects off Heroku. Here’s the playbook (and the 7 things that bit me every single time).
  7. The Claude Code → production checklist: 15 things that aren’t obvious until they bite you
  8. Your indie SaaS has zero working Postgres backups. Here’s the 20-minute fix.
  9. Your Stripe webhook is going to silently drop a paid customer. Here are the 4 patterns that catch it before they chargeback.
  10. Your crontab is silently failing. The 5 silent killers of VPS-based cron jobs.