Most Java codebases start with classes. Interface extraction happens later—reluctantly—when testing forces it or the second implementation appears. Flip this. Every component—use case, step, adapter—starts as an interface with a static factory method. Not as convention. For three specific reasons: 1. Substitutability without magic. Anyone can implement the interface. Testing becomes trivial—plain lambdas, no mocking framework, no @Mock annotations, no when().thenReturn() chains. Stubbing incomplete implementations during development is equally straightforward. The team working on inventory doesn't wait for the payment team. 2. Implementation isolation. No shared base classes. No abstract methods to override. No coupling between implementations. Each intersection between implementations is unnecessary coupling with corresponding maintenance overhead—up to needing deep understanding of two projects instead of one, with zero benefit. 3. Disposable implementation. The factory returns a lambda. Nothing references the implementation by class name. Nothing can. The implementation is replaceable by definition. The interface is the design artifact; the implementation is incidental. The compound effect: testing is configuration, refactoring is safe, complexity is bounded, incremental development is natural. The interface is what you design. The implementation is what you happen to write today. Full article with code examples: dev.to: https://lnkd.in/d93gqe4c Medium: https://lnkd.in/dnzYuS24 #java #softwarearchitecture #designpatterns #functionalprogramming #backend
Sergiy Yevtushenko’s Post
More Relevant Posts
-
🔥 String vs StringBuilder vs StringBuffer — Stop Using Them Blindly As Java developers, we use these daily. But as architects, we must ask: What is the memory impact? What is the concurrency model? What is the design intent? Let’s break it down 👇 1️⃣ String — Immutable by Design String is immutable. Every time you modify it, a new object is created in heap. String s = "Hello"; s = s + " World"; Why Java made it immutable? ✔ Thread-safe by default ✔ Secure (used in class loading, URLs, file paths) ✔ Safe for caching (String Pool) ✔ Hashcode cached (important for HashMap keys) 👉 Architect Rule: Use String when value should NOT change. 2️⃣ StringBuffer — Thread-Safe but Costly Mutable Methods are synchronized Introduced in Java 1.0 StringBuffer sb = new StringBuffer(); sb.append("Hello"); Every method call acquires a lock. That means performance overhead. 👉 Architect Rule: Use only when multiple threads modify the SAME instance. Otherwise, you’re paying for locking unnecessarily. 3️⃣ StringBuilder — The Practical Default Mutable NOT synchronized Faster Introduced in Java 1.5 StringBuilder sb = new StringBuilder(); sb.append("Hello"); 👉 Architect Rule: Use StringBuilder for loops, heavy concatenation, dynamic SQL building, etc. In most enterprise systems, this is the right choice. 🚨 The Common Performance Mistake String s = ""; for(int i = 0; i < 1000; i++) { s += i; // Creates 1000 objects } Better: StringBuilder sb = new StringBuilder(); for(int i = 0; i < 1000; i++) { sb.append(i); } 🧠 Final Architect Thinking It’s not about syntax. It’s about: Allocation strategy Concurrency ownership Memory churn Locking cost System scalability Good developers write working code. Good architects design predictable systems. #Java #Architecture #Performance #CleanCode #BackendDevelopment
To view or add a comment, sign in
-
📌 Singleton Design Pattern in Java The Singleton pattern ensures that a class has only one instance and provides a global access point to it. 1️⃣ Why Singleton? Used when: • Only one object is required • Shared resource management • Configuration handling • Logging frameworks • Database connection pools 2️⃣ Basic Singleton (Not Thread-Safe) class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } Problem: • Not thread-safe • Multiple threads may create multiple instances 3️⃣ Thread-Safe (Synchronized Method) public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } Issue: • Slower due to synchronization on every call 4️⃣ Double-Checked Locking (Efficient) private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } Why volatile? • Prevents instruction reordering • Ensures visibility across threads 5️⃣ Best Approach (Recommended) Using Enum: enum Singleton { INSTANCE; } Benefits: ✔ Thread-safe ✔ Prevents reflection attacks ✔ Serialization safe ✔ Simple and clean 🧠 Key Takeaway Singleton seems simple, but thread safety makes it complex. Understanding its implementations shows strong concurrency knowledge. #Java #DesignPatterns #Singleton #BackendDevelopment
To view or add a comment, sign in
-
Hook: 90% of high-latency production incidents in I/O-bound Java services trace back to blocking thread-pools. Java 21’s virtual threads + structured concurrency flips that model — simplifying code and reducing tail latency. Core Java: Master OOP, immutability, exceptions, collections, streams, and concurrency primitives. Practice: build a thread-safe bank transfer using synchronized, ReentrantLock, and java.util.concurrent.atomic types. Focus on defensive copying and Optional to avoid nulls. HTML & CSS: HTML5 semantics, accessible forms, ARIA, and responsive layouts with Flexbox/Grid. CSS: component-based styles, BEM or utility-first, and media queries. Practice: create an accessible dashboard layout and mobile-first styling. JavaScript & React: ES6+ features, promises, async/await, and event loop behavior. React: functional components, hooks, state isolation, useEffect for side effects, and React Query for server state. Practice: implement a debounced search with paginated results. Spring Boot: Layer controllers, services, repositories; use validation, centralized error handling, Actuator, and profiles. Practice: build a paginated CRUD service with DTO mapping and HATEOAS links. SQL & JDBC: Design normalized schemas, index strategically, write efficient joins and aggregates. JDBC: use prepared statements, batching, and connection pools
To view or add a comment, sign in
-
Runtime Reflection in Java — power with responsibility ⚠️ In Java, a Class object represents metadata of a class at runtime. Using Reflection, we can inspect and even modify class behavior while the application is running. 🔹 What is Reflection? Reflection allows us to: Access class metadata Inspect fields, methods, constructors Invoke methods dynamically Break access modifiers (private, protected) All this happens at runtime, not compile time. 🔹 Simple example Class<?> clazz = Class.forName("com.example.Singleton"); Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Object obj1 = constructor.newInstance(); Object obj2 = constructor.newInstance(); ⚠️ Even a private constructor can be accessed! 🔹 Real-world impact Reflection is heavily used by: Spring (DI, AOP, Proxy) Hibernate / JPA Jackson (JSON serialization) Framework auto-configuration But it also means: ❌ Breaks encapsulation ❌ Can violate design patterns (like Singleton) ❌ Slower than direct calls ❌ Harder to debug 🔹 Singleton + Reflection problem A classic Singleton can be broken at runtime using reflection. Best defense: ✅ enum Singleton (reflection-safe) ✅ Guard logic inside constructor ❌ Just private constructor is NOT enough 🧠 Architect’s mindset Reflection is like a master key Useful for frameworks, dangerous for business code. Use it when: Building frameworks Writing generic libraries Infrastructure-level logic Avoid it when: Writing domain/business logic Performance & safety are critical Final thought: Reflection makes Java powerful — discipline makes it safe. #Java #Reflection #JVM #SpringBoot #DesignPatterns #Singleton #BackendDevelopment #SoftwareArchitecture
To view or add a comment, sign in
-
Stop over‑provisioning thread pools: in real projects I’ve seen migrating blocking Spring Boot services to Java 21 virtual threads reduce p95 latency by 40–70% and cut thread counts by ~90%. If you’re still tuning fixed thread pools to chase tail latency, you’re fighting the wrong battle. Here’s a pragmatic, scannable 3‑step playbook for Spring Boot services running on Java 21+ that actually moves the needle. 1) Find the real blocking boundaries Use Flight Recorder, async-profiler, and Micrometer to map where requests block (DB calls, sync HTTP, file I/O). Don’t guess. Measure p95 and p99 with production‑like load and annotate traces with blocking spans. Concrete example: add a Micrometer Timer around your repository call and log percentile values. If jdbcTemplate calls dominate tail latency, that’s your boundary. 2) Convert execution model—keep code simple, change the scheduler If the work is blocking, prefer virtual threads over complex reactive rewrites. Create a virtual‑thread per task executor and run blocking operations there. Example (conceptual): ExecutorService vExec = Executors.newVirtualThreadPerTaskExecutor(); try { vExec.submit(() -> jdbcTemplate.query(...)).get(); } finally { vExec.close(); } This lets you keep existing imperative code while eliminating the need for large fixed pools. For fan‑out work, pair virtual threads with StructuredTaskScope for coordinated cancellation and failure handling. Concrete example: StructuredTaskScope.ShutdownOnFailure<Void> scope = new StructuredTaskScope.ShutdownOnFailure<>(); var f1 = scope.fork(() -> blockingCallA()); var f2 = scope.fork(() -> blockingCallB()); scope.join(); scope.throwIfFailed(); // gather f1.resultNow(), f2.resultNow() 3) Replace only where it matters and validate with end‑to‑end telemetry For external systems that cannot scale (legacy DB pools, remote services), use connection‑level strategies: smaller Hikari pools, timeouts, and backpressure. For high‑QPS read paths, consider R2DBC only if you need true non‑blocking I/O at the driver layer; otherwise virtual threads + sensible pool sizing is often the simplest, highest‑impact path. Concrete example: keep Hikari but reduce maxPoolSize and rely on virtual threads to handle concurrency; measure connection wait times with Hikari metrics. Final ingredients: enforce request timeouts, add structured logging for cancellation reasons, and make sure observability captures virtual threads (JFR + Micrometer tags). Question for the community: when you faced the reactive vs virtual‑thread choice, what operational signals (tail latency patterns, DB queueing metrics, deployment limits) drove your decision? Describe a specific incident and the tradeoffs you accepted — I’ll read and compare notes. Save this post for your next architectural review and check the full deep dive at https://lnkd.in/dC7ZNTVW 🚀
To view or add a comment, sign in
-
𝑫𝒂𝒚 1 𝒐𝒇 𝑴𝒚 𝑱𝒂𝒗𝒂 𝑰𝒏𝒕𝒆𝒓𝒗𝒊𝒆𝒘 𝑷𝒓𝒆𝒑𝒂𝒓𝒂𝒕𝒊𝒐𝒏 𝑱𝒐𝒖𝒓𝒏𝒆𝒚 – 45 𝑫𝒂𝒚𝒔 𝑪𝒉𝒂𝒍𝒍𝒆𝒏𝒈𝒆 Started a 45-day Java revision plan, and today focused on Core Java basics to strengthen fundamentals for real-world backend development and interviews. 🔹 𝙅𝙖𝙫𝙖 𝙁𝙪𝙣𝙙𝙖𝙢𝙚𝙣𝙩𝙖𝙡𝙨 𝙍𝙚𝙛𝙧𝙚𝙨𝙝𝙚𝙙 ✔ Object-Oriented Programming Language – Supports encapsulation, inheritance, polymorphism, and abstraction ✔ Developed by Sun Microsystems (now Oracle) ✔ Write Once, Run Anywhere (WORA) via JVM bytecode execution ✔ Heavily used in Enterprise Systems, Backend APIs, Microservices, and Android Development 💻 𝘿𝙚𝙚𝙥 𝘿𝙞𝙫𝙚: 𝙅𝘿𝙆 𝙫𝙨 𝙅𝙍𝙀 𝙫𝙨 𝙅𝙑𝙈 (𝙄𝙣𝙩𝙚𝙧𝙫𝙞𝙚𝙬 + 𝙋𝙧𝙖𝙘𝙩𝙞𝙘𝙖𝙡 𝙑𝙞𝙚𝙬) ✅ JDK (Java Development Kit) • Complete development toolkit • Contains JRE + Development tools (javac, debugger, tools.jar, etc.) • Used during development & compilation stage ✅ JRE (Java Runtime Environment) • Provides runtime libraries + JVM • Used only to run Java applications • No compilation capability ✅ JVM (Java Virtual Machine) • Executes bytecode → converts into machine-level instructions • Handles Garbage Collection, Memory Management, Thread Handling, Security • Makes Java platform independent 📊 𝙅𝙖𝙫𝙖 𝘿𝙖𝙩𝙖 𝙏𝙮𝙥𝙚𝙨 – 𝙒𝙝𝙮 𝙄𝙩 𝙈𝙖𝙩𝙩𝙚𝙧𝙨 𝙞𝙣 𝙍𝙚𝙖𝙡 𝙎𝙮𝙨𝙩𝙚𝙢𝙨 Java is strongly typed → prevents runtime surprises → improves code safety. 👉 Primitive Types (Performance Critical) Used in high-performance logic → less memory overhead 👉 Non-Primitive Types (Object Handling) Used in Collections, APIs, Database Entities, DTOs 📦 Variables – Memory & Scope Understanding ✔ Local Variables → Stack memory → method-level lifecycle ✔ Instance Variables → Heap memory → object-level state ✔ Static Variables → Class-level shared memory → useful in caching, counters, configs 🔐 𝘼𝙘𝙘𝙚𝙨𝙨 𝙈𝙤𝙙𝙞𝙛𝙞𝙚𝙧𝙨 – 𝙐𝙨𝙚𝙙 𝙞𝙣 𝙍𝙚𝙖𝙡 𝘼𝙧𝙘𝙝𝙞𝙩𝙚𝙘𝙩𝙪𝙧𝙚 • public → API exposure • private → Encapsulation & data protection • default → Package-level module design • protected → Inheritance-based architecture ⚙️ 𝙈𝙚𝙩𝙝𝙤𝙙𝙨 – 𝙍𝙚𝙪𝙨𝙖𝙗𝙞𝙡𝙞𝙩𝙮 + 𝘾𝙡𝙚𝙖𝙣 𝘼𝙧𝙘𝙝𝙞𝙩𝙚𝙘𝙩𝙪𝙧𝙚 A well-designed method improves: ✔ Code reusability ✔ Testability ✔ Maintainability ✔ Readability Includes: Access Modifier + Return Type + Method Name + Parameters + Business Logic 🖥️ 𝙋𝙧𝙖𝙘𝙩𝙞𝙘𝙚 𝙁𝙤𝙘𝙪𝙨 – 𝘾𝙤𝙢𝙢𝙖𝙣𝙙 𝙇𝙞𝙣𝙚 𝘼𝙧𝙜𝙪𝙢𝙚𝙣𝙩𝙨 Revisited Command Line Arguments → Useful for: • Passing runtime configs • Batch job execution • Automation scripts • Microservice startup parameters 💡 𝙆𝙚𝙮 𝙍𝙚𝙛𝙡𝙚𝙘𝙩𝙞𝙤𝙣 Revisiting basics always highlights how clean fundamentals lead to better system design, faster debugging, and more scalable code. Consistency > Speed. #Java #InterviewPreparation #BackendDevelopment #45DaysChallenge #JavaDeveloper #Programming #LearningJourney #SoftwareEngineering
To view or add a comment, sign in
-
-
Ever wondered how frameworks like Spring, Hibernate work their magic under the hood? A significant part of the answer lies in Java Reflection! Reflection is Java's incredible capability to inspect and manipulate its own classes, interfaces, fields, and methods at runtime, rather than compile-time. It's like giving your code an X-ray vision! 🔬 What can it do? - Inspect: Discover class names, modifiers, fields, and methods dynamically. - Access: Read and modify field values, even private ones. - Invoke: Call methods by their name, allowing for dynamic behavior. - Instantiate: Create new objects without explicitly knowing their class at compile time. Why is it powerful? It enables highly flexible and extensible applications. This dynamic capability is crucial for: - ⚙️ Frameworks: Dependency Injection, ORM mapping, dynamic proxies. - 🧪 Testing Tools: JUnit finding and executing test methods. - 🔄 Serialization: Converting objects to/from formats like JSON or XML. The Catch? While powerful, Reflection comes with trade-offs: - Performance Overhead: It's generally slower than direct calls. - Security Risks: Can break encapsulation, potentially exposing internal details. - Reduced Type Safety: Runtime errors instead of compile-time errors. #Java #JavaReflection #SoftwareDevelopment #Programming #TechInsights #BackendDevelopment
To view or add a comment, sign in
-
🧾 Java Records - When To Use & When To Avoid If you're using Java 17+ and still writing 50 lines for a simple DTO… you're missing out on Records. Introduced in Java 16, record is a special type for immutable data carriers. ✅ When To Use Records 1️⃣ For DTOs / API Request-Response Objects If your class only holds data → use record. public record UserDto(Long id, String name, String email) {} No getters. No constructor. No equals/hashCode/toString. All auto-generated. Perfect for: 1. REST APIs (Spring Boot controllers) 2. Kafka messages 3. Event payloads 4. Projection objects 2️⃣ When You Need Immutability Records are: 1. final 2. Fields are private final 3. Thread-safe by design (if fields are safe) 4. Great for clean architecture & functional-style programming. 3️⃣ Value-Based Objects If equality depends only on data → record is ideal. new UserDto(1L, "Jack", "jack@example.com") .equals(new UserDto(1L, "Jack", "jack@example.com")) // true ❌ When NOT To Use Records 1️⃣ Mutable Objects If your object needs setters → record is wrong choice. 2️⃣ JPA Entities Avoid using records as Hibernate entities. Why? 1. JPA needs no-arg constructor 2. Proxies don’t work well with final classes 3. Fields can’t be reassigned 4. Use normal class for @Entity. 3️⃣ Complex Business Logic If the class contains: 1. Heavy logic 2. Internal state changes 3. Inheritance requirements Stick to traditional class. ⚡ Quick Rule: 👉 If your class is just data → use record 👉 If your class has behavior & lifecycle → use class 👉 If you are preparing for Spring Boot backend interviews, connect & follow - I share short, practical backend concepts regularly. #SpringBoot #Backend #Java #JavaDeveloper #JavaBackend #BackendDevelopment #JavaProgramming #CleanCode #InterviewPrep #SoftwareEngineering #JavaTips
To view or add a comment, sign in
-
-
🧵 𝗝𝗮𝘃𝗮 𝗖𝗼𝗻𝗰𝘂𝗿𝗿𝗲𝗻𝗰𝘆 - Part 1: Process vs Thread When we talk about Java concurrency, we often jump straight into thread pools, locks, volatile, and Executors. But before going deeper into higher-level abstractions, it helps to understand how this works at the system level. And that starts with understanding the distinction between a process and a thread. 🔹 𝗣𝗿𝗼𝗰𝗲𝘀𝘀 When you run a Java application, the OS creates a new process that runs the JVM. The OS gives the process: - Its own virtual memory space - Its own memory mappings - Its own resources - A strong isolation boundary Inside that process, the JVM creates the Java heap, static data and thread stacks. No other process can directly access this memory. Because of that isolation, process switching is expensive: - Memory mappings must change - Page tables are switched - CPU state is fully saved and restored 🔹 𝗧𝗵𝗿𝗲𝗮𝗱 Threads live inside a process. They share the same heap, code and static variables. But each thread has its own stack, program counter and registers. That means: Local variables → live on the stack → safe Objects & static data → live on the heap → shared When switching between threads of the same process: - Memory mapping stays the same - Only registers and stack pointer change Much lighter operation. Faster, but now we have shared memory. And shared memory means coordination. So, there's a tradeoff - 𝗣𝗿𝗼𝗰𝗲𝘀𝘀𝗲𝘀 → 𝗜𝘀𝗼𝗹𝗮𝘁𝗶𝗼𝗻 + 𝗦𝗮𝗳𝗲𝘁𝘆 𝗧𝗵𝗿𝗲𝗮𝗱𝘀 → 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 + 𝗦𝗵𝗮𝗿𝗲𝗱 𝗺𝗲𝗺𝗼𝗿𝘆 This is Part 1 of the Java concurrency series. In the next post, I’ll break down exactly which parts of your code live on the stack, which live on the heap, and what that means for thread safety. Follow along and feel free to add or refine anything I miss.
To view or add a comment, sign in
-
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development