Lesson 10: Common Workflows#
In this final lesson, you’ll learn real-world workflows that combine everything you’ve learned into productive development patterns.
Learning Objectives#
By the end of this lesson, you’ll be able to:
- Implement new features efficiently
- Fix bugs systematically
- Refactor code safely
- Work with existing codebases
- Optimize your development workflow
Workflow 1: Implementing a New Feature#
Scenario: Add User Authentication#
Let’s implement a complete authentication feature.
Step 1: Plan the Implementation#
Create a TODO list (use comments or :TodoWrite):
- Create User model
- Create AuthService
- Implement login method
- Implement logout method
- Add password hashing
- Write testsStep 2: Create Package Structure#
:!mkdir -p src/main/java/com/example/auth/{model,service}
:!mkdir -p src/test/java/com/example/auth/serviceStep 3: Create Model (TDD Style)#
Write test first:
<Space>ff→AuthServiceTest.java- Write failing test:
package com.example.auth.service;
import org.junit.Test;
import static org.junit.Assert.*;
public class AuthServiceTest {
@Test
public void testLogin() {
AuthService auth = new AuthService();
boolean result = auth.login("user", "password");
assertTrue(result);
}
}<Space>jt- Test fails (class doesn’t exist)
Create implementation:
<Space>ff→AuthService.java- Quick implementation:
package com.example.auth.service;
public class AuthService {
public boolean login(String username, String password) {
return false; // Minimal code
}
}<Space>jt- Test still fails- Fix implementation
<Space>jt- Test passes ✓
Step 4: Iterate#
Repeat TDD cycle for each feature:
- Logout
- Password validation
- Token generation
- Session management
Step 5: Integration#
<Space>ff→ OpenMain.java- Add authentication:
AuthService auth = new AuthService();
if (auth.login(username, password)) {
// Continue
}- Use
gdto jump between related files - Use
<Ctrl-o>to jump back
Step 6: Final Testing#
- Run all tests:
:!mvn test - Fix any failures
- Check coverage
- Commit changes
Workflow 2: Bug Fixing#
Scenario: NullPointerException in Production#
Step 1: Reproduce the Bug#
- Read stack trace
- Find the line:
UserService.java:42 <Space>ff→ Type “UserServ:42” → Opens file at line 42
Step 2: Write Failing Test#
@Test
public void testGetUserWithNullId() {
UserService service = new UserService();
User result = service.getUser(null);
assertNull(result); // Should handle gracefully
}Run: <Space>jt - Fails with NPE
Step 3: Debug#
<Space>db- Set breakpoint at line 42<Space>dc- Run in debug mode- Inspect variables
- Find null check is missing
Step 4: Fix#
public User getUser(Long id) {
if (id == null) {
return null; // Or throw exception
}
return repository.findById(id);
}Step 5: Verify#
<Space>jt- Test passes ✓- Run full suite:
:!mvn test - Check no regressions
Step 6: Find All Similar Issues#
<Space>fg- Grep for similar patterns- Search:
findById( - Review each occurrence
- Add null checks where needed
Workflow 3: Refactoring#
Scenario: Extract Service from Controller#
Step 1: Identify Code to Extract#
Current messy controller:
public class UserController {
public void createUser(String name, String email) {
// Validation
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Name required");
}
// Business logic
User user = new User(name, email);
user.setCreatedAt(new Date());
// Persistence
database.save(user);
}
}Step 2: Write Tests for Current Behavior#
@Test
public void testCreateUser() {
UserController controller = new UserController();
controller.createUser("Alice", "alice@example.com");
// Verify behavior
}Run: <Space>jT - All pass (baseline)
Step 3: Create Service Class#
<Space>ff→ CreateUserService.java- Use Visual mode to select business logic
<Space>ca→ “Extract to method”
Move to new service:
public class UserService {
public User createUser(String name, String email) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Name required");
}
User user = new User(name, email);
user.setCreatedAt(new Date());
return user;
}
}Step 4: Update Controller#
public class UserController {
private UserService userService = new UserService();
public void createUser(String name, String email) {
User user = userService.createUser(name, email);
database.save(user);
}
}Step 5: Run Tests#
<Space>jT - All still pass ✓
Refactoring successful!
Step 6: Update References#
- Cursor on
UserService gr- Find all references- Verify all usages make sense
- Update as needed
Workflow 4: Working with Unknown Codebase#
Scenario: Fix Bug in Unfamiliar Project#
Step 1: Find Entry Point#
<Space>fg- Search for “main”- Find
Main.java - Read through main method
Step 2: Trace Execution#
- Cursor on method call
gd- Go to definition- Repeat to build mental map
- Use
<Ctrl-o>to backtrack
Step 3: Search for Domain Terms#
<Space>fg- Grep for business terms- Search: “payment”, “order”, etc.
- Find relevant code sections
Step 4: Explore Package Structure#
<Space>ne- Open file tree- Browse package organization
- Understand layers (model, service, controller)
Step 5: Find Related Code#
<Space>ws- Workspace symbols- Search for class names
- Use
grto find usages
Step 6: Document as You Go#
Create notes file:
# Codebase Notes
## Architecture
- MVC pattern
- Service layer handles business logic
- Repository pattern for data access
## Key Classes
- OrderService - Main business logic
- PaymentGateway - External integration
- UserRepository - Database access
## Issues Found
- No null checks in OrderService.process()
- Missing validation in PaymentGatewayWorkflow 5: Code Review#
Scenario: Review Pull Request#
Step 1: Check Out Branch#
git fetch origin
git checkout feature-branchStep 2: Open Changed Files#
git diff main --name-onlyOpen each file: <Space>ff
Step 3: Review Changes#
<Space>fg- Search for patterns- Look for:
- Missing null checks
- No error handling
- Hardcoded values
- Missing tests
Step 4: Test Locally#
- Run tests:
:!mvn test - Check coverage
- Run application
- Test edge cases
Step 5: Leave Comments#
Create notes or use GitHub CLI:
gh pr review --comment "Add null check on line 42"Workflow 6: Performance Optimization#
Scenario: Slow API Endpoint#
Step 1: Profile#
Add timing logs:
long start = System.currentTimeMillis();
result = expensiveOperation();
long duration = System.currentTimeMillis() - start;
System.out.println("Duration: " + duration + "ms");Step 2: Identify Bottleneck#
- Run with debug logging
- Find slow section
gdto jump to implementation
Step 3: Optimize#
Common fixes:
- Add caching
- Reduce database queries
- Use bulk operations
- Lazy load data
Step 4: Measure Impact#
- Add before/after timing
- Run tests:
<Space>jT - Compare results
Step 5: Check for Regressions#
- Run full test suite
- Verify correctness maintained
- Check edge cases still work
Workflow 7: Dependency Update#
Scenario: Upgrade Library Version#
Step 1: Update pom.xml#
<dependency>
<groupId>com.example</groupId>
<artifactId>library</artifactId>
<version>2.0.0</version> <!-- was 1.0.0 -->
</dependency>Step 2: Check Breaking Changes#
- Read changelog
- Search usage:
<Space>fg→ “import com.example.library” - Review all usages
Step 3: Update API Calls#
- Find deprecated methods
grto find all usages- Update each one
- Use code actions for quick fixes
Step 4: Run Tests#
:!mvn test- Fix failures
- Update tests as needed
Step 5: Manual Testing#
- Run application
- Test affected features
- Verify behavior unchanged
Workflow 8: Git Integration#
Scenario: Feature Development with Git#
Step 1: Create Feature Branch#
git checkout -b feature/user-authenticationStep 2: Make Changes#
- Implement feature
- Use all Neovim tools
- Test thoroughly
Step 3: Stage Changes#
:!git add src/main/java/com/example/auth
:!git statusStep 4: Commit#
:!git commit -m "Add user authentication feature"Step 5: Push and Create PR#
git push -u origin feature/user-authentication
gh pr create --title "Add user authentication"Power User Tips#
Tip 1: Custom Keybindings#
Add frequent commands to config:
vim.keymap.set('n', '<leader>tr', ':!mvn test<CR>', { desc = 'Run tests' })
vim.keymap.set('n', '<leader>tc', ':!mvn clean<CR>', { desc = 'Clean' })Tip 2: Project-Specific Config#
Create .nvim.lua in project root:
-- Project-specific settings
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4Tip 3: Terminal Integration#
Use toggleterm for integrated terminal:
<C-7> - Toggle terminal (Ctrl+7)
<Space>gb - Run Gradle build
<Space>gc - Run Gradle compile
<Space>gg - Open LazygitTip 4: Session Management#
Save your workspace:
:mksession! ~/sessions/myproject.vimRestore later:
nvim -S ~/sessions/myproject.vimTip 5: Quick Navigation Marks#
Set global marks for frequent files:
mM - Mark Main.java
mS - Mark Service
mT - Mark Test
'M - Jump to Main
'S - Jump to Service
'T - Jump to TestProductivity Checklist#
Daily workflow checklist:
-
<Space>ff- Quick file navigation -
gd/gr- Jump to code -
<Space>ca- Use code actions -
<Space>jt- Run tests frequently -
K- Read documentation -
<Space>db- Debug when stuck -
:w- Save often - Commit early and often
Resources#
- Quick Reference - Keybinding cheat sheet
- Neovim Documentation
- nvim-java GitHub
- Vim Tutor - Built-in Vim tutorial
Congratulations!#
You’ve completed all 10 lessons of the Neovim4j tutorial!
You now know how to:
- Set up Java projects
- Write code with intelligent completion
- Navigate large codebases
- Debug applications
- Write and run tests
- Use real-world development workflows
Next Steps#
- Practice daily - Use Neovim for all Java development
- Customize your config - Make it yours
- Learn more Vim - Run
:Tutorfor Vim fundamentals - Explore plugins - Add tools as needed
- Join community - GitHub discussions, Reddit r/neovim
Happy coding with Neovim4j!