IDEs β€” IntelliJ IDEA, Eclipse, and VS Code

How semantic indexing actually works under the hood, where AI-assisted coding fits (and where it doesn't), remote debugging against a running container, and choosing the right tool for the job

← Back to Index

What is an IDE β€” and Why Does It Exist?

Before IDEs, Java development meant a text editor plus a terminal: write the file, run javac, read the compiler's line-number errors, switch back to the editor, repeat. Every piece of context β€” which classes exist, whether a method call is valid, whether a variable is null-safe β€” lived only in the developer's head. An IDE's actual job is to make that context machine-visible: it parses your entire codebase (and its dependencies) into an in-memory symbol index so it can answer "does this method exist / is this type correct / where is this used" in milliseconds, continuously, before you ever run a compile.

// BEFORE β€” editor + terminal, zero live feedback
$ vim OrderService.java
$ javac OrderService.java
OrderService.java:14: error: cannot find symbol
    customerRepository.findByEmail(email)
                        ^
  symbol:   method findByEmail(String)
  location: variable customerRepository of type CustomerRepository
// The error is discovered only after a full compile β€” and all you get
// is a line number, not a fix.

// AFTER β€” IDE with live semantic indexing
// The IDE flags the missing method the instant you type it β€” a red
// underline, no compile step required β€” and offers to generate the
// method stub directly on CustomerRepository via a quick fix.
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    Optional<Customer> findByEmail(String email);   // generated in one keystroke
}

This is the actual value proposition of an IDE, and it's worth stating precisely because it's what separates a real IDE from "a text editor with syntax highlighting": a full semantic model of your code, kept live as you type, that powers code completion, error detection, navigation, and mechanical refactoring β€” all without a compile-run cycle.

The 2026 Java IDE Landscape β€” and How Each One Actually Sees Your Code

The three mainstream options solve the "semantic model" problem with genuinely different architectures, and that architectural choice is why their behavior on large codebases diverges:

  • IntelliJ IDEA builds its own proprietary semantic model β€” the PSI (Program Structure Interface) β€” a full syntax and type tree maintained entirely by JetBrains, independent of any external compiler. This is why its refactoring and code-completion tend to stay accurate even across huge multi-module Maven/Gradle projects.
  • Eclipse uses the JDT (Java Development Tools) β€” its own incremental Java compiler that re-parses and re-type-checks only the files that changed. JDT predates the Language Server Protocol and was built directly into Eclipse's architecture.
  • VS Code has no built-in Java understanding at all. Its Java support comes from wrapping Eclipse's own JDT as a Language Server (JDT.LS), communicated over the Language Server Protocol (LSP) β€” the same underlying engine as Eclipse, exposed through a different editor shell.
Where AI-native editors (Cursor, Windsurf) actually fit

AI-native editors built as VS Code forks use the exact same JDT.LS language server for Java semantics that stock VS Code uses β€” their AI layer is additive, not a replacement for the underlying type-checking engine. For a large Spring Boot or Jakarta EE codebase, this matters: the quality of "does this compile / is this type correct" comes from JDT.LS regardless of which AI-native shell sits on top of it, and JDT.LS is historically less complete than IntelliJ's PSI on deep, multi-module refactors. Choose an AI-native editor for its agentic workflow, not under the assumption that it understands enterprise Java better than IntelliJ does.

IntelliJ IDEA

IntelliJ IDEA, developed by JetBrains, is the default choice for most professional Java teams β€” its PSI-based engine gives it the strongest refactoring and framework-aware completion of the three mainstream options.

Editions

  • Community Edition β€” free, open-source, covers core Java SE, JUnit, and basic Maven/Gradle support
  • Ultimate Edition β€” paid, adds Spring/Spring Boot-aware completion, Jakarta EE tooling, database tools, and JavaScript/TypeScript support for full-stack work

Key Features

// Framework-aware completion (Ultimate) β€” knows Spring bean wiring, not just syntax
@Service
public class OrderService {
    private final CustomerRepository customerRepository;

    public OrderService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }
    // Gutter icon links this constructor directly to the Spring bean graph β€”
    // click it to jump to every place this bean is injected.
}

// Real-time null-safety analysis
Customer customer = customerRepository.findById(id).orElse(null);
customer.getEmail();  // flagged: "Method invocation may produce NullPointerException"

// Quick Fix (Alt+Enter) on a missing null check
public void notify(Customer customer) {
    // Alt+Enter here offers to wrap in an Objects.requireNonNull or an if-null guard
}

Live Templates

// Type the abbreviation and press Tab to expand

// "psvm" β†’
public static void main(String[] args) { }

// "sout" β†’
System.out.println();

// "iter" β†’ enhanced for-loop over the inferred collection type
for (OrderItem item : order.getItems()) { }
Postfix completion β€” write the expression first, the structure follows
  • order.getItems().for → generates a for-each loop over the items
  • customer.null → generates an if (customer == null) guard
  • result.nn → generates a not-null check

Essential Keyboard Shortcuts

ActionWindows/LinuxmacOS
Search EverywhereDouble ShiftDouble Shift
Find ClassCtrl+NCmd+O
Code CompletionCtrl+SpaceCtrl+Space
Quick FixAlt+EnterOption+Enter
Refactor ThisCtrl+Alt+Shift+TCtrl+T
Run / DebugShift+F10 / Shift+F9Ctrl+R / Ctrl+D
Reformat CodeCtrl+Alt+LCmd+Option+L

Eclipse IDE

Eclipse is a free, open-source IDE built on the OSGi plugin architecture (Equinox) β€” every feature, including Java support itself, is a plugin. This is why it's historically been the IDE of choice for teams needing deep customization or vendor application-server tooling built as Eclipse plugins.

Eclipse Packages

  • Eclipse IDE for Java Developers β€” core Java SE development
  • Eclipse IDE for Enterprise Java and Web Developers β€” Jakarta EE, JAX-RS, application-server integration

Key Features

// Content Assist (Ctrl+Space)
Order order = orderRepository.findById(orderId);
order.  // Ctrl+Space lists every accessible member of Order

// Quick Fix (Ctrl+1) on an unresolved type
// Hover the error, Ctrl+1 β†’ "Import 'Order' (com.shop.model)"

// Organize Imports (Ctrl+Shift+O) β€” adds missing, removes unused

Essential Keyboard Shortcuts

ActionShortcut
Content AssistCtrl+Space
Quick FixCtrl+1
Open TypeCtrl+Shift+T
Organize ImportsCtrl+Shift+O
Rename RefactorAlt+Shift+R
Run / DebugCtrl+F11 / F11

Workspace Structure

workspace/
β”œβ”€β”€ .metadata/                  # Eclipse settings β€” never commit this
β”œβ”€β”€ order-service/
β”‚   β”œβ”€β”€ .project                # project descriptor
β”‚   β”œβ”€β”€ .classpath              # classpath entries
β”‚   └── src/main/java/com/shop/order/
└── customer-service/
    └── ...

Visual Studio Code

VS Code is a lightweight, cross-platform editor turned into a Java IDE entirely through extensions β€” its Java intelligence, as covered above, comes from wrapping Eclipse's JDT as a language server rather than a purpose-built Java engine.

Essential Java Extensions

  • Extension Pack for Java (Microsoft) β€” bundles Language Support for Java (Red Hat), Debugger for Java, Test Runner for Java, and Maven for Java
  • Spring Boot Extension Pack β€” bean navigation, application.properties completion

VS Code Java Shortcuts

ActionWindows/LinuxmacOS
Command PaletteCtrl+Shift+PCmd+Shift+P
Go to FileCtrl+PCmd+P
Quick FixCtrl+.Cmd+.
Format DocumentShift+Alt+FShift+Option+F
When VS Code is the right choice
  • Polyglot work spanning Java, front-end, and infra config in one window
  • Constrained hardware where a full IDE's memory footprint is a real cost
  • Quick edits to a service you don't own the full module graph of

For deep, day-to-day work inside a large Spring/Jakarta EE monorepo, the weaker refactoring engine (Section 1) is a real cost, not a style preference.

AI-Assisted Development in the IDE

Every mainstream IDE now ships an integrated AI layer β€” GitHub Copilot (IntelliJ, Eclipse, VS Code), JetBrains AI Assistant and its autonomous coding agent Junie, and AI-native editors like Cursor and Windsurf with agentic "auto-accept" modes. These sit on top of the semantic engines from Section 1 β€” they don't replace them, and the distinction matters in production.

// A plausible-looking AI suggestion for a repository query method
@Query("SELECT o FROM Order o WHERE o.customer.email = :email")
List<Order> findOrdersForCustomer(@Param("email") String email);

// Looks correct and compiles. What the assistant didn't know:
// this project's convention is soft-deleted orders (deletedAt IS NULL),
// enforced everywhere else via a @Where clause the assistant never saw
// because it wasn't in the file it was editing. The generated method
// silently returns cancelled/deleted orders too.
The real production risk is not "wrong code" β€” it's plausible code

AI suggestions fail differently from a junior developer's mistakes: they compile, they follow correct Java syntax and common patterns, and they read as confident. The risk is specifically the categories a compiler cannot catch β€” business-rule omissions (soft-delete conventions, tenant isolation filters), N+1 queries hidden behind a reasonable-looking method signature, and @Transactional boundaries that don't match the surrounding service's actual rollback rules. Review AI-generated code with the same rigor as a human pull request β€” never with less, on the assumption that "it compiled" means "it's correct."

Where it genuinely helps

  • Boilerplate the IDE can't template-generate β€” mapping DTOs, writing parameterized test cases across a matrix of inputs
  • Explaining unfamiliar code in a legacy module before you refactor it
  • Drafting a first pass at a Javadoc or a commit message, always edited afterward

IDE Comparison

FeatureIntelliJ IDEAEclipseVS Code
PriceFree (Community) / Paid (Ultimate)FreeFree
Java engineProprietary PSIJDT (native)JDT via LSP
Refactoring depthExcellentGoodBasic–Good
Framework awareness (Spring/Jakarta EE)Excellent (Ultimate)Good (plugins)Good (extensions)
Memory footprintHeavyModerateLight
Built-in AI assistantJetBrains AI / JunieVia pluginCopilot / extensions

Debugging in IDEs

Local debugging

public void processOrder(Order order) {
    BigDecimal total = BigDecimal.ZERO;         // breakpoint here

    for (OrderItem item : order.getItems()) {
        total = total.add(item.getUnitPrice()
                    .multiply(BigDecimal.valueOf(item.getQuantity())));
    }

    applyDiscount(order, total);         // Step Into (F7 / F5) to follow the call
    order.setTotal(total);               // Step Over to stay at this level
}

// Conditional breakpoint: right-click the breakpoint β†’ add a condition
// Example: item.getQuantity() > 100 β€” stops only for bulk-order edge cases

// Watch expression: order.getItems().size() β€” evaluated live at every stop

Remote debugging β€” attaching to a running container

In a microservices e-commerce stack, the service misbehaving in staging is usually already running inside a Docker container or a Kubernetes pod β€” you don't reproduce it locally, you attach to it.

# Enable the JDWP debug agent when starting the JVM inside the container
JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"

# docker-compose.yml β€” expose the debug port alongside the app port
services:
  order-service:
    environment:
      - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
    ports:
      - "8080:8080"
      - "5005:5005"

// IntelliJ: Run β†’ Edit Configurations β†’ + β†’ Remote JVM Debug β†’ host, port 5005
// Eclipse: Run β†’ Debug Configurations β†’ Remote Java Application
suspend=y and an exposed debug port are real production risks

suspend=y halts the entire JVM at startup until a debugger attaches β€” fine for a throwaway local container, catastrophic if accidentally left on a service behind a load balancer that health-checks it. Just as important: a JDWP port has no authentication whatsoever β€” anyone who can reach 5005 can attach a debugger and execute arbitrary code in that JVM's context. Never expose a debug port beyond a locked-down internal network, and never enable it by default in a staging or production image β€” add it transiently, debug, remove it.

Project Configuration

Setting up the JDK

// IntelliJ IDEA: File β†’ Project Structure β†’ Project β†’ SDK
// Eclipse: Window β†’ Preferences β†’ Java β†’ Installed JREs

// VS Code β€” settings.json, multiple JDKs for polyglot service work
{
    "java.configuration.runtimes": [
        { "name": "JavaSE-17", "path": "/opt/jdk-17" },
        { "name": "JavaSE-21", "path": "/opt/jdk-21", "default": true }
    ]
}

Importing a multi-module project

// e-commerce-platform/
//   β”œβ”€β”€ order-service/pom.xml
//   β”œβ”€β”€ customer-service/pom.xml
//   └── shared-domain/pom.xml   ← common Customer/Order/Product model

// IntelliJ: File β†’ Open β†’ select the root pom.xml β†’ "Open as Project"
//   (multi-module Maven reactor is detected and each module becomes a
//   separate IntelliJ module with correct inter-module dependencies)

// Eclipse: File β†’ Import β†’ Maven β†’ Existing Maven Projects β†’ select all modules
// VS Code: open the root folder β€” the Java extension detects the reactor automatically

Productivity and Team-Wide Configuration

Share formatting rules β€” don't rely on each developer's personal IDE settings

A per-developer code style setting is a guaranteed source of noisy diffs. Commit an .editorconfig at the repo root β€” IntelliJ, Eclipse, and VS Code all read it natively β€” so indentation and line endings are enforced identically regardless of which IDE a teammate uses.

# .editorconfig β€” committed at repo root, read by all three IDEs
root = true

[*.java]
indent_style = space
indent_size = 4
max_line_length = 120
insert_final_newline = true
trim_trailing_whitespace = true

Code generation, on the actual domain

// Generate constructor, accessors, equals/hashCode, toString
// (Alt+Insert in IntelliJ, Source menu in Eclipse)
public class Customer {
    private Long id;
    private String email;
    private String fullName;
    private LocalDate registeredAt;

    // Generated constructor
    public Customer(Long id, String email, String fullName, LocalDate registeredAt) {
        this.id = id;
        this.email = email;
        this.fullName = fullName;
        this.registeredAt = registeredAt;
    }

    // Generated equals/hashCode β€” based on id, the natural identity for an entity
    @Override
    public boolean equals(Object o) { /* ... */ }

    @Override
    public int hashCode() { return Objects.hash(id); }
}

Generated equals()/hashCode() on a JPA entity should key off the identifier, not every field β€” see Entity Relationships for why field-based equality breaks inside collections once Hibernate's persistence context is involved.

Choosing the Right IDE by Use Case

Enterprise Spring/Jakarta EE codebaseIntelliJ IDEA Ultimate
Learning JavaIntelliJ Community or Eclipse
Jakarta EE with a vendor application serverEclipse IDE for Enterprise Java
Polyglot service, limited resourcesVS Code
Agentic AI-driven workflowsCursor/Windsurf, or VS Code + Copilot β€” same JDT.LS engine underneath

Best Practices and Common Pitfalls

βœ… Do

  • Commit an .editorconfig so formatting is consistent regardless of which IDE a teammate uses
  • Review AI-generated code with the same rigor as a human pull request β€” "it compiled" is not "it's correct"
  • Enable remote debug ports transiently and only over a locked-down internal network β€” never leave suspend=y on a health-checked service
  • Learn your IDE's mechanical refactorings (rename, extract method, change signature) β€” they update every call site, a manual find-and-replace does not
  • Use conditional breakpoints and watches instead of adding temporary System.out.println statements

❌ Don't

  • Don't assume an AI-native editor understands enterprise Java better than IntelliJ β€” the underlying language server is frequently the same JDT.LS engine VS Code uses
  • Don't accept AI-suggested repository or query methods without checking them against project-wide conventions (soft deletes, tenant filters, fetch strategy) the assistant couldn't see
  • Don't expose a JDWP debug port on any environment reachable from outside a trusted network β€” it has no authentication
  • Don't rely on personal IDE code-style settings as the team's formatting source of truth β€” it produces noisy, unrelated diffs

Interview Questions

πŸŽ“ Junior level

Q: What does an IDE actually provide beyond a text editor with syntax highlighting?
A live semantic model of the codebase β€” a symbol index built and kept up to date as you type β€” that answers "does this method exist," "is this type correct," and "where is this used" instantly, without running the compiler. This is what powers code completion, real-time error detection, and mechanical refactoring.

Q: What's the difference between code completion and a quick fix?
Code completion suggests what you might type next as you're typing it (a method name, a variable). A quick fix reacts to an already-flagged problem β€” a missing import, a possible null dereference β€” and offers to resolve it automatically, usually triggered while the cursor is on the error.

Q: What is a conditional breakpoint, and why is it better than adding a temporary System.out.println?
A conditional breakpoint only pauses execution when a boolean expression you specify is true β€” for example, only when an order quantity exceeds 100. It requires no code change, so there's nothing to remember to remove afterward, and it lets you inspect the full call stack and every variable in scope at that exact moment, not just whatever value you thought to print.

πŸ”₯ Senior level

Q: IntelliJ IDEA and VS Code (with the Java extension pack) can both show completion and errors for the same Java file. Architecturally, why does IntelliJ's refactoring tend to hold up better on large, multi-module codebases?
VS Code's Java support comes from wrapping Eclipse's JDT as a Language Server (JDT.LS) accessed over the Language Server Protocol β€” a general-purpose interface designed to serve many languages with a common contract. IntelliJ instead maintains its own proprietary semantic model, the PSI, built specifically for JetBrains' own refactoring and inspection engines with no intermediary protocol. On a small file the difference is invisible; on a large multi-module Maven/Gradle reactor with deep cross-module references, IntelliJ's purpose-built engine historically resolves and updates every call site more completely during a mechanical refactor than an LSP-mediated tool does. Neither is "wrong" β€” they're solving the same problem with a different amount of Java-specific investment in the engine itself.

Q: Your team enables an AI coding assistant in agent/auto-accept mode. What is the actual production risk on a Spring Data JPA e-commerce codebase, and what practice mitigates it?
The risk is not that the generated code fails to compile β€” it almost always does compile, which is exactly the danger. The assistant has no visibility into project-wide conventions that live outside the file it's editing: a soft-delete filter applied via @Where elsewhere in the entity, a tenant-isolation predicate every other query includes, or the specific @Transactional(rollbackOn = ...) convention the team uses for checked exceptions. A generated repository method can be syntactically perfect and still silently violate one of these rules, producing a bug that no compiler error and no obvious code smell will surface β€” it will surface as subtly wrong data in production. The mitigation is treating every AI-generated change exactly like a human-authored pull request: full review, tests run, and no exception carved out just because a suggestion "looked right" and compiled cleanly.

Q: Explain what happens when you attach a remote debugger via JDWP to a running JVM, the difference between suspend=y and suspend=n, and the concrete security exposure of leaving the port open.
JDWP (Java Debug Wire Protocol) opens a socket the JVM listens on for a debugger to connect to; once attached, the debugger can inspect and modify the running program's state, set breakpoints, and β€” critically β€” execute arbitrary bytecode in that JVM's security context. suspend=y halts the entire JVM at startup until a debugger connects, which is fine for a disposable local container but means a load-balanced service would simply never come up (and fail health checks) if this flag were left on by accident. The deeper issue is that JDWP has no authentication mechanism at all: anyone who can reach that port over the network can attach and run code as that process, with no credentials required. This is why a debug port must never be reachable from outside a trusted, locked-down network, and should be enabled transiently for an active debugging session rather than baked permanently into a staging or production image.