Posts Tagged ‘Devoxx’
[DevoxxFR 2025] Be More Productive with IntelliJ IDEA
Presented by Marit van Dijk (JetBrains)
IntelliJ IDEA is renowned for being a powerful and intelligent Integrated Development Environment (IDE) designed to help developers stay in the flow and maximize their productivity. With its rich set of features, including a smart editor, powerful refactorings, seamless navigation, and integrated tools for various technologies, IntelliJ IDEA aims to provide a comprehensive development experience without the need to leave the IDE. Marit van Dijk from JetBrains showcases how to leverage these capabilities to become a happier and more productive developer.
Marit’s talk delves into the myriad of features that contribute to developer productivity in IntelliJ IDEA. She highlights how the IDE supports various workflows and provides tools for everything from writing and reading code to debugging, testing, and working with databases and version control systems.
Staying in the Flow with a Smart IDE
Maintaining focus and staying in the “flow state” is crucial for developer productivity. Frequent context switching, interruptions, and wrestling with inefficient tools can easily break this flow. Marit van Dijk emphasizes that IntelliJ IDEA is designed to minimize these distractions and help developers stay focused on writing code.
She showcases the IDE’s intelligent code editor, which provides smart code completion, code analysis, and quick fixes. Features like intention actions and context-aware suggestions help developers write code more efficiently and accurately, reducing the need to manually search for syntax or API usage.
Powerful Refactorings and Navigation
Refactoring code is an essential part of maintaining code quality and improving the design of an application. IntelliJ IDEA offers a wide range of powerful automated refactorings that can significantly speed up this process and reduce the risk of introducing errors. Marit demonstrates some of the most useful refactorings, such as renaming variables or methods, extracting methods or interfaces, and changing method signatures.
Seamless navigation within a codebase is also critical for understanding existing code and quickly jumping between different parts of the project. Marit highlights IntelliJ IDEA’s navigation features, such as jumping to declarations or usages, navigating through recent files and locations, and searching for symbols or files by name. These features allow developers to explore their codebase efficiently and find the information they need quickly.
Integrated Tools for a Comprehensive Workflow
Modern software development involves working with a variety of tools and technologies beyond just the code editor. IntelliJ IDEA integrates with a wide range of popular tools, providing a unified experience within the IDE. Marit van Dijk showcases how IntelliJ IDEA seamlessly integrates with:
- Build Tools: Maven and Gradle for managing project dependencies and building applications.
- Version Control Systems: Git and others for managing code changes and collaborating with team members.
- Databases: Tools for connecting to databases, Browse schemas, writing and executing queries, and managing data.
- Test Tools: Integration with testing frameworks like JUnit and TestNG for writing, running, and debugging tests.
- Debugging: A powerful debugger for stepping through code, inspecting variables, and diagnosing issues.
By providing these integrated tools, IntelliJ IDEA allows developers to perform most of their tasks without leaving the IDE, minimizing context switching and improving productivity.
AI-Powered Assistance
In addition to its traditional features, IntelliJ IDEA is also incorporating AI-powered assistance to further enhance developer productivity. Marit touches upon features like the AI Assistant, which can provide code suggestions, generate documentation, and even explain complex code snippets.
She might also mention tools sounding like “Juny”, a coding agent that can perform more complex coding tasks, such as generating boilerplate code or creating prototypes. These AI-powered features aim to automate repetitive tasks and provide developers with intelligent assistance throughout their workflow.
Conclusion: A Happier and More Productive Developer
Marit van Dijk concludes by reinforcing the message that leveraging the features of IntelliJ IDEA can make developers happier and more productive. By providing a smart editor, powerful refactorings, seamless navigation, integrated tools, and AI-powered assistance, the IDE helps developers stay in the flow, write better code, and focus on delivering value.
The talk encourages developers to explore the full potential of IntelliJ IDEA and customize it to fit their specific workflows. By making the most of the IDE’s capabilities, developers can significantly improve their efficiency and enjoy a more productive and fulfilling coding experience.
Hashtags: #DevoxxFR2025 #IntelliJIDEA #IDE #DeveloperProductivity #Java #Coding #Refactoring #Debugging #AI #JetBrains #MaritvanDijk
[DevoxxBE2024] How JavaScript Happened: A Short History of Programming Languages
In an engaging session at Devoxx Belgium 2024, Mark Rendle traced the evolution of programming languages leading to JavaScript’s creation in 1995. Titled “How JavaScript Happened: A Short History of Programming Languages,” the talk blended humor and history, from Ada Lovelace’s 1840s program to JavaScript’s rapid development for Netscape Navigator 2.0. Despite a brief battery scare during the presentation, Rendle’s storytelling and FizzBuzz examples across languages captivated the audience, offering insights into language design and JavaScript’s eclectic origins.
The Dawn of Programming
Rendle began in the 1830s with Ada Lovelace, who wrote the first program for Charles Babbage’s unbuilt Analytical Engine, introducing programming notation 120 years before computers existed. The 1940s saw programmable machines like Colossus, built to crack German ciphers, and ENIAC, programmed by women who deciphered its operation without manuals. These early systems, configured via patch cables, laid the groundwork for modern computing, though programming remained labor-intensive.
The Rise of High-Level Languages
The 1950s marked a shift with Fortran, created by John Backus to simplify machine code translation for IBM’s 701 mainframe. Fortran introduced if statements, the asterisk for multiplication (due to punch card limitations), and the iterator variable i
, still ubiquitous today. ALGOL 58 and 60 followed, bringing block structures, if-then-else, and BNF grammar, formalized by Backus. Lisp, developed by John McCarthy, introduced first-class functions, the heap, and early garbage collection, while Simula pioneered object-oriented programming with classes and inheritance.
From APL to C and Beyond
Rendle highlighted APL’s concise syntax, enabled by its unique keyboard and dynamic typing, influencing JavaScript’s flexibility. The 1960s and 70s saw BCPL, B, and C, with C introducing curly braces, truthiness, and the iconic “hello world” program. Smalltalk added reflection, virtual machines, and the console, while ML introduced functional programming concepts like arrow functions. Scheme, a simplified Lisp, directly influenced JavaScript’s initial design as a browser scripting language, shaped to compete with Java applets.
JavaScript’s Hasty Creation
In 1995, Brendan Eich created JavaScript in ten days for Netscape Navigator 2.0, initially as a Scheme-like language with a DOM interface. To counter Java applets, it adopted a C-like syntax and prototypal inheritance (inspired by Self), as classical inheritance wasn’t feasible in Scheme. Rendle humorously speculated on advising Eich to add static typing and classical inheritance, noting JavaScript’s roots in Fortran, ALGOL, Lisp, and others. Despite its rushed origins, JavaScript inherited a rich legacy, from Fortran’s syntax to Smalltalk’s object model.
The Legacy and Future of JavaScript
Rendle concluded by reflecting on JavaScript’s dominance, driven by its browser integration, and its ongoing evolution, with features like async/await (from C#) and proposed gradual typing. He dismissed languages like COBOL and Pascal for lacking influential contributions, crediting BASIC for inspiring programmers despite adding little to language design. JavaScript, a synthesis of 70 years of innovation, continues to evolve, shaped by decisions from 1955 to today, proving no language is immune to historical influence.
Links:
Hashtags: #JavaScript #ProgrammingHistory #MarkRendle #DevoxxBE2024
[DevoxxBE2024] Thinking Like an Architect
In a reflective talk at Devoxx Belgium 2024, Gregor Hohpe, a veteran architect, shared insights from two decades of experience in “Thinking Like an Architect.” Hohpe debunked the myth of architects as all-knowing decision-makers, instead portraying them as “IQ boosters” who enhance team decision-making through models, metaphors, and multi-level communication. Despite a minor issue with a clicker during the presentation, his engaging delivery and relatable examples, like the “architect elevator,” offered practical strategies for navigating complex organizational and technical landscapes.
Connecting Levels with the Architect Elevator
Hohpe introduced the “architect elevator,” a metaphor for architects’ role in bridging organizational layers—from developers to executives. He argued that the most valuable architects connect business strategy to technical implementation, translating complex trade-offs into terms executives understand without oversimplifying. For example, automation and frequent releases (developer priorities) enable security and cost-efficiency (executive concerns). This connection counters the isolation caused by layered organizations, where management may assume all is well due to buzzwords like Kubernetes, while developers operate with unchecked freedom.
Seeing More Dimensions in Decision-Making
Architects expand solution spaces by revealing additional dimensions, Hohpe explained. Using a sketch of a cylinder mistaken as a circle or rectangle, he showed how architects resolve debates—like speed versus quality—by introducing options like automated testing. At AWS, Hohpe tackled vendor lock-in by framing it as a two-dimensional trade-off: switching costs versus benefits. This approach, inspired by Adrian Cockcroft’s analogy of marriage as “accepted lock-in,” fosters rational discussions, avoiding binary thinking and helping teams find balanced solutions.
Selling Options to Defer Decisions
Hohpe likened architects to options traders, deferring decisions to reduce uncertainty. For instance, standard APIs allow language flexibility, sacrificing some protocol options to gain adaptability. In a financial firm, he explained this to executives using options trading, noting that options’ value rises with volatility—a concept they instantly grasped via the Black-Scholes formula. This metaphor underscores architecture’s increasing relevance in uncertain environments, aligning it with agile methodologies, which thrive under similar conditions. However, options come at the cost of complexity, a trade-off architects must weigh.
Zooming In and Out for System-Wide Perspective
To tackle complexity, architects must zoom in and out, balancing local and global optima. Hohpe illustrated this with two systems using identical components but different connections, yielding opposite characteristics (e.g., latency versus resilience). Local optimization, like perfecting a single component, often fails to ensure system-wide success, as seen in operations where “all lights are green, but nothing works.” By viewing systems holistically, architects ensure decisions align with broader goals, avoiding pitfalls like excessive layering that propagates changes unnecessarily.
Using Models to Navigate Uncertainty
Hohpe emphasized models as architects’ best tools for simplifying complexity. Comparing geocentric and heliocentric solar system models, he showed how the right model makes decisions obvious, even if imperfect. Models vary by purpose—topographical maps for hiking, population density for logistics—requiring architects to choose based on the question at hand. In uncertain environments, models shine by forcing assumptions, enabling scenario-based planning (e.g., low, medium, high user loads). Hohpe urged architects to avoid absolutes, embracing shades of gray to find optimal trade-offs.
Links:
- Devoxx Belgium 2024
- The Architect Elevator
- Cloud Strategy by Gregor Hohpe
- Adrian Cockcroft’s Blog
- Black-Scholes Formula
Hashtags: #SoftwareArchitecture #ArchitectMindset #AgileArchitecture #DevoxxBE2024
[DevoxxBE2024] Project Leyden: Improving Java’s Startup Time by Per Minborg, Sébastien Deleuze
Per Minborg and Sébastien Deleuze delivered an insightful joint presentation at Devoxx Belgium 2024, unveiling the transformative potential of Project Leyden to enhance Java application startup time, warmup, and footprint. Per, from Oracle’s Java Core Library team, and Sébastien, a Spring Framework core committer at Broadcom, explored how Leyden shifts computation across time to optimize performance. Despite minor demo hiccups, such as Wi-Fi-related delays, their talk combined technical depth with practical demonstrations, showcasing how Spring Boot 3.3 leverages Leyden’s advancements, cutting startup times significantly and paving the way for future Java optimizations.
Understanding Project Leyden’s Mission
Project Leyden, an open-source initiative under OpenJDK, aims to address long-standing Java performance challenges: startup time, warmup time, and memory footprint. Per explained startup as the duration from launching a program to its first useful operation, like displaying “Hello World” or serving a Spring app’s initial request. Warmup, conversely, is the time to reach peak performance via JIT compilation. Leyden’s approach involves shifting computations earlier (e.g., at build time) or later (e.g., via lazy initialization) while preserving Java’s dynamic nature. Unlike GraalVM Native Image or Project CRaC, which sacrifice dynamism for speed, Leyden maintains compatibility, allowing developers to balance performance and flexibility.
Class Data Sharing (CDS) and AOT Cache: Today’s Solutions
Per introduced Class Data Sharing (CDS), a feature available since JDK 5, and its evolution into the Ahead-of-Time (AOT) Cache, a cornerstone of Leyden’s strategy. CDS preloads JDK classes, while AppCDS, introduced in JDK 10, extends this to application classes. The AOT Cache, an upcoming enhancement, stores class objects, resolved linkages, and method profiles, enabling near-instant startup. Sébastien demonstrated this with a Spring Boot Pet Clinic application, reducing startup from 3.2 seconds to 800 milliseconds using CDS and AOT Cache. The process involves a training run to generate the cache, which is then reused for faster deployments, though it requires consistent JVM and classpath configurations.
Spring Boot’s Synergy with Leyden
Sébastien highlighted the collaboration between the Spring and Leyden teams, initiated after a 2023 JVM Language Summit case study. Spring Boot 3.3 introduces features to simplify CDS and AOT Cache usage, such as extracting executable JARs into a CDS-friendly layout. A demo showed how a single command extracts the JAR, runs a training phase, and generates a cache, which is then embedded in a container image. This reduced startup times by up to 4x and memory usage by 20% when combined with Spring’s AOT optimizations. Sébastien also demonstrated how AOT Cache retains JIT “warmness,” enabling near-peak performance from startup, though a minor performance plateau gap is being addressed.
Future Horizons and Trade-offs
Looking ahead, Leyden plans to introduce stable values, a hybrid between mutable and immutable fields, offering final-like performance with flexible initialization. Per emphasized that Leyden avoids the heavy constraints of GraalVM (e.g., limited reflection) or CRaC (e.g., Linux-only, security concerns with serialized secrets). While CRaC achieves millisecond startups, its lifecycle complexities and security risks limit adoption. Leyden’s AOT Cache, conversely, offers significant gains (2–4x faster startups) with minimal constraints, making it ideal for most use cases. Developers can experiment with Leyden’s early access builds to optimize their applications, with further enhancements like code cache storage on the horizon.
Links:
Hashtags: #ProjectLeyden #Java #SpringBoot #AOTCache #CDS #StartupTime #JVM #DevoxxBE2024 #PerMinborg #SébastienDeleuze
Renovate/Dependabot: How to Take Control of Dependency Updates
At Devoxx France 2024, held in April at the Palais des Congrès in Paris, Jean-Philippe Baconnais and Lise Quesnel, consultants at Zenika, presented a 30-minute talk titled Renovate/Dependabot, ou comment reprendre le contrôle sur la mise à jour de ses dépendances. The session explored how tools like Dependabot and Renovate automate dependency updates, reducing the tedious and error-prone manual process. Through a demo and lessons from open-source and client projects, they shared practical tips for implementing Renovate, highlighting its benefits and pitfalls. 🚀
The Pain of Dependency Updates
The talk opened with a relatable skit: Lise, working on a side project (a simple Angular 6 app showcasing women in tech), admitted to neglecting updates due to the effort involved. Jean-Philippe emphasized that this is a common issue across projects, especially in microservice architectures with numerous components. Updating dependencies is critical for:
- Security: Applying patches to reduce exploitable vulnerabilities.
- Features: Accessing new functionalities.
- Bug Fixes: Benefiting from the latest corrections.
- Performance: Leveraging optimizations.
- Attractiveness: Using modern tech stacks (e.g., Node 20 vs. Node 8) to appeal to developers.
However, the process is tedious, repetitive, and complex due to transitive dependencies (e.g., a median of 683 for NPM projects) and cascading updates, where one update triggers others.
Automating with Dependabot and Renovate
Dependabot (acquired by GitHub) and Renovate (from Mend) address this by scanning project files (e.g., package.json
, Maven POM, Dockerfiles) and opening pull requests (PRs) or merge requests (MRs) for available updates. These tools:
- Check registries (NPM, Maven Central, Docker Hub) for new versions.
- Provide visibility into dependency status.
- Save time by automating version checks, especially in microservice setups.
- Enhance reactivity, critical for applying security patches quickly.
Setting Up the Tools
Dependabot: Configured via a dependabot.yml
file, specifying ecosystems (e.g., NPM), directories, and update schedules (e.g., weekly). On GitHub, it integrates natively via project settings. GitLab users can use a similar approach.
# dependabot.yml version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly"
Renovate: Configured via a renovate.json
file, extending default presets. It supports GitHub and GitLab via apps or CI/CD pipelines (e.g., GitLab CI with a Docker image). For self-hosted setups, Renovate can run as a Docker container or Kubernetes CronJob.
# renovate.json { "extends": [ "config:recommended" ] }
In their demo, Jean-Philippe and Lise showcased Renovate on a GitLab project, using a .gitlab-ci.yml
pipeline to run Renovate on a schedule, creating MRs for updates like rxjs
(from 6.3.2 to 6.6.7).
Customizing Renovate
Renovate’s strength lies in its flexibility through presets and custom configurations:
- Presets: Predefined rules (e.g.,
npm:unpublishSafe
waits 3 days before proposing updates). Presets can extend others, forming a hierarchy (e.g.,config:recommended
extends base presets). - Custom Presets: Organizations can define reusable configs in a dedicated repository (e.g.,
renovate-config
) and apply them across projects.
// renovate-config/default.json { "extends": [ "config:recommended", ":npm" ] }
- Grouping Updates: Combine related updates (e.g., all ESLint packages) using
packageRules
or presets likegroup:recommendedLinters
to reduce PR noise.
{ "packageRules": [ { "matchPackagePatterns": ["^eslint"], "groupName": "eslint packages" } ] }
- Dependency Dashboard: An issue tracking open, rate-limited, or ignored MRs, activated via the
dependencyDashboard
field or preset.
Going Further: Automerge and Beyond
To streamline updates, Renovate supports automerge, automatically merging MRs if the pipeline passes, relying on robust tests. Options include:
automerge: true
for all updates.automergeType: "pr"
orstrategy
for specific behaviors.- Presets like
automerge:patch
for patch updates only.
The demo showed an automerged rxjs
update, triggering a new release (v1.2.1) via semantic-release, tagged, and deployed to Google Cloud Run. A failed Angular update (due to a major version gap) demonstrated how failing tests block automerge, ensuring safety.
Renovate can also update itself and its configuration (e.g., deprecated fields) via the config:migration
preset, creating MRs for self-updates.
Lessons Learned and Recommendations
From their experiences, Jean-Philippe and Lise shared key tips:
- Manage PR Overload: Limit concurrent PRs (e.g.,
prConcurrentLimit: 5
) and group related updates to reduce noise. - Use Schedules: Run Renovate at off-peak times (e.g., nightly) to avoid overloading CI runners and impacting production deployments.
- Ensure Robust Tests: Automerge relies on trustworthy tests; weak test coverage can lead to broken builds.
- Balance Frequency: Frequent runs catch updates quickly but risk conflicts; infrequent runs may miss critical patches.
- Monitor Resource Usage: Excessive pipelines can strain runners and increase costs in autoscaling environments (e.g., cloud platforms).
- Handle Transitive Dependencies: Renovate manages them like direct dependencies, but cascading updates require careful review.
- Support Diverse Ecosystems: Renovate works well with Java (e.g., Spring Boot, Quarkus), Scala, and NPM, with grouping to manage high-dependency ecosystems like NPM.
- Internal Repositories: Configure Renovate to scan private registries by specifying URLs.
- Major Updates: Use presets to stage major updates incrementally, avoiding risky automerge for breaking changes.
Takeaways
Jean-Philippe and Lise’s talk highlighted how Dependabot and Renovate transform dependency management from a chore to a streamlined process. Their demo and practical advice showed how Renovate’s flexibility—via presets, automerge, and dashboards—empowers teams to stay secure and up-to-date, especially in complex microservice environments. However, success requires careful configuration, robust testing, and resource management to avoid overwhelming teams or infrastructure. 🌟
[DevoxxFR 2024] Debugging Your Salary: Winning Strategies for Successful Negotiation
At Devoxx France 2024, Shirley Almosni Chiche, an independent IT recruiter and career agent, delivered a dynamic session titled “Debuggez votre salaire ! Mes stratégies gagnantes pour réussir sa négociation salariale.” With over a decade of recruitment experience, Shirley unpacked the complexities of salary negotiation, offering actionable strategies to overcome common obstacles. Through humor, personas, and real-world insights, she empowered developers to approach salary discussions with confidence and preparation, transforming a daunting process into a strategic opportunity.
Navigating the Salary Minefield
Shirley opened with a candid acknowledgment: salary discussions are fraught with tension, myths, and frustrations. Drawing from her role at Build RH, her recruitment firm, she likened salary negotiation to a high-stakes race, where candidates endure lengthy recruitment processes only to face disappointing offers. Common employer excuses—“we must follow the salary grid,” “we can’t pay more than existing staff,” or “the budget is tight”—often derail negotiations, leaving candidates feeling undervalued.
To frame her approach, Shirley introduced six “bugs” that justify low salaries, each paired with a persona representing typical employer archetypes. These included the rigid “Big Corp” manager enforcing salary grids, the team-focused “Didier Deschamps” avoiding pay disparities, and the budget-conscious “François Damiens” citing financial constraints. Other personas, like the overly technical “Elon” scrutinizing code, the relentless negotiator “Patrick,” and the discriminatory “Hubert,” highlighted diverse challenges candidates face.
Shirley shared market insights, noting a 2023–2024 tech slowdown with 200,000 global layoffs, reduced venture funding, and a shift toward cost-conscious industries like banking and retail. This context, she argued, demands strategic preparation to secure fair compensation.
Countering the Bugs: Tactical Responses
For each bug, Shirley offered counter-arguments rooted in empathy and alignment with employer priorities. Against the salary grid, she advised exploring non-salary benefits like profit-sharing or PERCO plans, common in large firms. Using a “mirror empathy” tactic, candidates can frame salary needs in the employer’s language—e.g., linking pay to productivity. Challenging outdated grids by highlighting market research or internal surveys also strengthens arguments.
For the “Didier Deschamps” persona, Shirley suggested emphasizing unique skills (e.g., full-stack expertise in a backend-heavy team) to justify higher pay without disrupting team cohesion. Proposing contributions like speaking at conferences or aiding recruitment can further demonstrate value. She shared a success story where a candidate engaged the team directly, securing a better offer through collective dialogue.
When facing “François Damiens” and financial constraints, Shirley recommended focusing on risk mitigation. For startups, candidates can negotiate stock options or bonuses, arguing that their expertise accelerates product delivery, saving recruitment costs. Highlighting polyvalence—combining skills like development, data, and security—positions candidates as multi-role assets, justifying premium pay.
For technical critiques from “Elon,” Shirley urged immediate feedback post-interview to address perceived weaknesses. If gaps exist, candidates should negotiate training opportunities to ensure long-term fit. Pointing out evaluation mismatches (e.g., testing frontend skills for a backend role) can redirect discussions to relevant strengths.
Against “Patrick,” the negotiator, Shirley advised setting firm boundaries—two rounds of negotiation max—to avoid endless haggling. Highlighting project flaws tactfully and aligning expertise with business goals can shift the dynamic from adversarial to collaborative.
Addressing Discrimination: A Sobering Reality
Shirley tackled the “Hubert” persona, representing discriminatory practices, with nuance. Beyond gender pay gaps, she highlighted biases against older candidates, neurodivergent individuals, those with disabilities, and career switchers. Citing her mother’s experience as a Maghrebi woman facing a 20% pay cut, Shirley acknowledged the harsh realities for marginalized groups.
Rather than dismissing discriminatory offers outright, she advised viewing them as career stepping stones. Candidates can leverage such roles for training or experience, using “mirror empathy” to negotiate non-salary benefits like remote work or learning opportunities. While acknowledging privilege, Shirley urged resilience, encouraging candidates to “lend an ear to learning” and rebound from setbacks.
Mastering Preparation: Anticipating the Negotiation
Shirley emphasized proactive preparation as the cornerstone of successful negotiation. Understanding one’s relationship with money—shaped by upbringing, traumas, or social pressures—is critical. Some candidates undervalue themselves due to impostor syndrome, while others see salary as a status symbol or family lifeline. Recognizing these drivers informs negotiation strategies.
She outlined key preparation steps:
- Job Selection: Target roles within your expertise and in high-paying sectors (e.g., cloud, security) for better leverage. Data roles can yield 7–13% salary gains.
- Market Research: Use resources like Choose Your Boss or APEC barometers to benchmark salaries. Shirley noted Île-de-France salaries exceed regional ones by 10–15K, with a 70K ceiling for seniors in 2023.
- Company Analysis: Assess financial health via LinkedIn or job ad longevity. Long-posted roles signal negotiation flexibility.
- Recruiter Engagement: Treat initial recruiter calls as data-gathering opportunities, probing team culture, hiring urgency, and technical expectations.
- Value Proposition: Highlight impact—product roadmaps, technical migrations, or team mentoring—early in interviews to set a premium tone.
Shirley cautioned against oversharing personal financial details (e.g., current salary or expenses) during salary discussions. Instead, provide a specific range (e.g., “around 72K”) based on market data and role demands. Mentioning parallel offers tactfully can spur employers to act swiftly.
Sealing the Deal: Confidence and Coherence
In the final negotiation phase, Shirley advised a 48-hour reflection period after receiving an offer, consulting trusted peers for perspective. Counteroffers should be fact-based, reiterating interview insights and using empathetic language. Timing matters—avoid Mondays or late Fridays for discussions.
Citing APEC data, Shirley noted that 80% of executives who negotiate are satisfied, with 65% securing their target salary or higher. She urged candidates to remain consistent, avoiding last-minute demands that erode trust. Beyond salary, consider workplace culture, inclusion, and work-life balance to ensure long-term fit.
Shirley closed with a rallying call: don’t undervalue your skills or settle for less. By blending preparation, empathy, and resilience, candidates can debug their salary negotiations and secure rewarding outcomes.
Links:
Hashtags: #SalaryNegotiation #DevoxxFrance #CareerDevelopment #TechRecruitment
[DevoxxFR 2024] Staff Engineer: A Vital Role in Technical Leadership
My former estimated colleague François Nollen, a technical expert at SNCF Connect & Tech, delivered an engaging talk at Devoxx France 2024 on the role of the Staff Engineer. Often overshadowed by the more familiar Engineering Manager position, the Staff Engineer role is gaining traction as a critical path for technical leadership without management responsibilities. François shared his journey and insights into how Staff Engineers operate at SNCF Connect, offering a blueprint for developers aspiring to influence organizations at scale. This post explores the role’s responsibilities, its impact, and its relevance in modern tech organizations.
Defining the Staff Engineer Role
The Staff Engineer role, rooted in Silicon Valley’s tech giants, represents a senior technical contributor who drives impact across multiple teams without managing them directly. François described Staff Engineers as versatile problem-solvers, blending deep technical expertise with strong collaboration skills. Unlike Engineering Managers, who focus on team management, Staff Engineers tackle complex technical challenges, set standards, and foster innovation. At SNCF Connect, they are called “Technical Expertise Referents,” reflecting their role in guiding technical strategy and mentoring teams.
A Day in the Life
Staff Engineers at SNCF Connect enjoy significant autonomy, with no fixed daily tasks. François outlined a typical day, which begins with monitoring communication channels like Slack to identify team challenges. They contribute code, conduct reviews, and drive strategic initiatives, such as defining best practices or evaluating technical risks. Unlike team-bound developers, Staff Engineers operate at an organizational level, collaborating with engineering, HR, and communication teams to align technical and business goals. This broad scope requires a balance of technical depth and interpersonal finesse.
Impact and Collaboration
The influence of a Staff Engineer stems from their expertise and ability to inspire trust, not formal authority. François highlighted their role in unblocking teams, accelerating projects, and shaping technical strategy alongside Principal Engineers. At SNCF Connect, Staff Engineers work as a collective, amplifying their impact on cross-cutting initiatives like DevOps and continuous delivery. This collaborative approach contrasts with traditional roles like architects, who may be disconnected from delivery, making Staff Engineers integral to dynamic, agile environments.
Is It Right for You?
François posed a reflective question: is the Staff Engineer role suited for everyone? It demands extensive technical experience, organizational awareness, and strong communication skills. Developers who thrive on solving complex problems, mentoring others, and driving systemic change without managing teams may find this path rewarding. For organizations, Staff Engineers offer a framework to retain and empower experienced developers, avoiding the pitfalls of promoting them into unsuitable management roles, as per the Peter Principle.
Links:
Hashtags: #StaffEngineer #TechnicalLeadership #DevoxxFrance #FrançoisNollen #SNCFConnect #Engineering #Agile
[DevoxxFR 2024] Going AOT: Mastering GraalVM for Java Applications
Alina Yurenko 🇺🇦 , a developer advocate at Oracle Labs, captivated audiences at Devoxx France 2024 with her deep dive into GraalVM’s ahead-of-time (AOT) compilation for Java applications. With a passion for open-source and community engagement, Alina explored how GraalVM’s Native Image transforms Java applications into compact, high-performance native executables, ideal for cloud environments. Through demos and practical guidance, she addressed building, testing, and optimizing GraalVM applications, debunking myths and showcasing its potential. This post unpacks Alina’s insights, offering a roadmap for adopting GraalVM in production.
GraalVM and Native Image Fundamentals
Alina introduced GraalVM as both a high-performance JDK and a platform for AOT compilation via Native Image. Unlike traditional JVMs, GraalVM allows developers to run Java applications conventionally or compile them into standalone native executables that don’t require a JVM at runtime. This dual capability, built on over a decade of research at Oracle Labs, offers Java’s developer productivity alongside native performance benefits like faster startup and lower resource usage. Native Image, GA since 2019, analyzes an application’s bytecode at build time, identifying reachable code and dependencies to produce a compact executable, eliminating unused code and pre-populating the heap for instant startup.
The closed-world assumption underpins this process: all application behavior must be known at build time, unlike the JVM’s dynamic runtime optimizations. This enables aggressive optimizations but requires careful handling of dynamic features like reflection. Alina demonstrated this with a Spring Boot application, which started in 1.3 seconds on GraalVM’s JVM but just 47 milliseconds as a native executable, highlighting its suitability for serverless and microservices where startup speed is critical.
Benefits Beyond Startup Speed
While fast startup is a hallmark of Native Image, Alina emphasized its broader advantages, especially for long-running applications. By shifting compilation, class loading, and optimization to build time, Native Image reduces runtime CPU and memory usage, offering predictable performance without the JVM’s warm-up phase. A Spring Pet Clinic benchmark showed Native Image matching or slightly surpassing the JVM’s C2 compiler in peak throughput, a testament to two years of optimization efforts. For memory-constrained environments, Native Image excels, delivering up to 2–3x higher throughput per memory unit at heap sizes of 512MB to 1GB, as seen in throughput density charts.
Security is another benefit. By excluding unused code, Native Image reduces the attack surface, and dynamic features like reflection require explicit allow-lists, enhancing control. Alina also noted compatibility with modern Java frameworks like Spring Boot, Micronaut, and Quarkus, which integrate Native Image support, and a community-maintained list of compatible libraries on the GraalVM website, ensuring broad ecosystem support.
Building and Testing GraalVM Applications
Alina provided a practical guide for building and testing GraalVM applications. Using a Spring Boot demo, she showcased the Native Maven plugin, which streamlines compilation. The build process, while resource-intensive for large applications, typically stays within 2GB of memory for smaller apps, making it viable on CI/CD systems like GitHub Actions. She recommended developing and testing on the JVM, compiling to Native Image only when adding dependencies or in CI/CD pipelines, to balance efficiency and validation.
Dynamic features like reflection pose challenges, but Alina outlined solutions: predictable reflection works out-of-the-box, while complex cases may require JSON configuration files, often provided by frameworks or libraries like H2. A centralized GitHub repository hosts configs for popular libraries, and a tracing agent can generate configs automatically by running the app on the JVM. Testing support is robust, with JUnit and framework-specific tools like Micronaut’s test resources enabling integration tests in Native mode, often leveraging Testcontainers.
Optimizing and Future Directions
To achieve peak performance, Alina recommended profile-guided optimizations (PGO), where an instrumented executable collects runtime profiles to inform a final build, combining AOT’s predictability with JVM-like insights. A built-in ML model predicts profiles for simpler scenarios, offering 6–8% performance gains. Other optimizations include using the G1 garbage collector, enabling machine-specific flags, or building static images for minimal container sizes with distroless images.
Looking ahead, Alina highlighted two ambitious GraalVM projects: Layered Native Images, which pre-compile base images (e.g., JDK or Spring) to reduce build times and resource usage, and GraalOS, a platform for deploying native images without containers, eliminating container overhead. Demos of a LangChain for Java app and a GitHub crawler using Java 22 features showcased GraalVM’s versatility, running seamlessly as native executables. Alina’s session underscored GraalVM’s transformative potential, urging developers to explore its capabilities for modern Java applications.
Links:
Hashtags: #GraalVM #NativeImage #Java #AOT #AlinaYurenko #DevoxxFR2024
[DevoxxFR 2024] Super Tech’Rex World: The Assembler Strikes Back
Nicolas Grohmann, a developer at Sopra Steria, took attendees on a nostalgic journey through low-level programming with his talk, “Super Tech’Rex World: The Assembler Strikes Back.” Over five years, Nicolas modified Super Mario World, a 1990 Super Nintendo Entertainment System (SNES) game coded in assembler, transforming it into a custom adventure featuring a dinosaur named T-Rex. Through live coding and engaging storytelling, he demystified assembler, revealing its principles and practical applications. His session illuminated the inner workings of 1990s consoles while showcasing assembler’s relevance to modern computing.
A Retro Quest Begins
Nicolas opened with a personal anecdote, recounting how his project began in 2018, before Sopra Steria’s Tech Me Up community formed in 2021. He described this period as the “Stone Age” of his journey, marked by trial and error. His goal was to hack Super Mario World, a beloved SNES title, replacing Mario with T-Rex, coins with pixels (a Sopra Steria internal currency), and mushrooms with certifications that boost strength. Enemies became “pirates,” symbolizing digital adversaries.
To set the stage, Nicolas showcased the SNES, a 1990s console with a CPU, ROM, and RAM—components familiar to modern developers. He launched an emulator to demonstrate Super Mario World, highlighting its mechanics: jumping, collecting items, and battling enemies. A modified ROM revealed his custom version, where T-Rex navigated a reimagined world. This demo captivated the audience, blending nostalgia with technical ambition.
For the first two years, Nicolas relied on community tools to tweak graphics and levels, such as replacing Mario’s sprite with T-Rex. However, as a developer, he yearned to contribute original code, prompting him to learn assembler. This shift marked the “Age of Discoveries,” where he tackled the language’s core concepts: machine code, registers, and memory addressing.
Decoding Assembler’s Foundations
Nicolas introduced assembler’s essentials, starting with machine code, the binary language of 0s and 1s that CPUs understand. Grouped into 8-bit bytes (octets), a SNES ROM comprises 1–4 megabytes of such code. He clarified binary and hexadecimal systems, noting that hexadecimal (0–9, A–F) compacts binary for readability. For example, 15 in decimal is 1111 in binary and 0F in hexadecimal, while 255 (all 1s in a byte) is FF.
Next, he explored registers, small memory locations within the CPU, akin to global variables. The accumulator, a key register, stores a single octet for operations, while the program counter tracks the next instruction’s address. These registers enable precise control over a program’s execution.
Memory addressing, Nicolas’s favorite concept, likens SNES memory to a city. Each octet resides in a “house” (address 00–FF), within a “street” (page 00–FF), in a “neighborhood” (bank 00–FF). This structure yields 16 megabytes of addressable memory. Addressing modes—long (full address), absolute (bank preset), and direct page (bank and page preset)—optimize code efficiency. Direct page, limited to 256 addresses, is ideal for game variables, streamlining operations.
Assembler, Nicolas clarified, isn’t a single language but a family of instruction sets tailored to CPU types. Opcodes, mnemonic instructions like LDA (load accumulator) and STA (store accumulator), translate to machine code (e.g., LDA becomes A5 for direct page). These opcodes, combined with addressing modes, form the backbone of assembler programming.
Live Coding: Empowering T-Rex
Nicolas transitioned to live coding, demonstrating assembler’s practical application. His goal: make T-Rex invincible and alter gameplay to challenge pirates. Using Super Mario World’s memory map, a community-curated resource, he targeted address 7E0019, which tracks the player’s state (0 for small, 1 for large). By writing LDA #$01
(load 1) and STA $19
(store to 7E0019), he ensured T-Rex remained large, immune to damage. The #
denotes an immediate value, distinguishing it from an address.
To nerf T-Rex’s jump, Nicolas manipulated controller inputs at addresses 7E0015 and 7E0016, which store button states as bitmasks (e.g., the leftmost bit for button B, used for jumping). Using LDA $15
and AND #$7F
(bitwise AND with 01111111), he cleared the B button’s bit, disabling jumps while preserving other controls. He applied this to both addresses, ensuring consistency.
To restore button B for firing projectiles, Nicolas used 7E0016, which flags buttons pressed in a single frame. With LDA $16
, AND #$80
(isolating B’s bit), and BEQ
(branch if zero to skip firing), he ensured projectiles spawned only on B’s press. A JSL
(jump to subroutine long) invoked a community routine to spawn a custom sprite—a projectile that moves right and destroys enemies.
These demos showcased assembler’s precision, leveraging memory maps and opcodes to reshape gameplay. Nicolas’s iterative approach—testing, tweaking, and re-running—mirrored real-world debugging.
Mastering the Craft: Hooks and the Stack
Reflecting on 2021, the “Modern Age,” Nicolas shared how he mastered code insertion. Since modifying Super Mario World’s original ROM risks corruption, he used hooks—redirects to free memory spaces. A tool inserts custom code at an address like $A00, replacing a segment (e.g., four octets) with a JSL
(jump subroutine long) to a hook. The hook preserves original code, jumps to the custom code via JML
(jump long), and returns with RTL
(return long), seamlessly integrating modifications.
The stack, a RAM region for temporary data, proved crucial. Managed by a stack pointer register, it supports opcodes like PHA
(push accumulator) and PLA
(pull accumulator). JSL
pushes the return address before jumping, and RTL
pops it, ensuring correct returns. This mechanism enabled complex routines without disrupting the game’s flow.
Nicolas introduced index registers X and Y, which support opcodes like LDX
and STX
. Indexed addressing (e.g., LDA $00,X
) adds X’s value to an address, enabling dynamic memory access. For example, setting X to 2 and using LDA $00,X
accesses address $02.
Conquering the Game and Beyond
In a final demo, Nicolas teleported T-Rex to the game’s credits by checking sprite states. Address 7E14C8 and the next 11 addresses track 12 sprite slots (0 for empty). Using X as a counter, he looped through LDA $14C8,X
, branching with BNE
(branch if not zero) if a sprite exists, or decrementing X with DEX
and looping with BPL
(branch if positive). If all slots are empty, a JSR
(jump subroutine) triggers the credits, ending the game.
Nicolas concluded with reflections on his five-year journey, likening assembler to a steep but rewarding climb. His game, nearing release on the Super Mario World hacking community’s site, features space battles and a 3D boss, pushing SNES limits. He urged developers to embrace challenging learning paths, emphasizing that persistence yields profound satisfaction.
Links:
Hashtags: #Assembler #DevoxxFrance #SuperNintendo #RetroGaming #SopraSteria #LowLevelProgramming
[Devoxx FR 2024] Mastering Reproducible Builds with Apache Maven: Insights from Hervé Boutemy
Introduction
In a recent presentation, Hervé Boutemy, a veteran Maven maintainer, Apache Software Foundation member, and Solution Architect at Sonatype, delivered a compelling talk on reproducible builds with Apache Maven. With over 20 years of experience in Java, CI/CD, DevOps, and software supply chain security, Hervé shared his five-year journey to make Maven builds reproducible, a critical practice for achieving the highest level of trust in software, as defined by SLSA Level 4. This post dives into the key concepts, practical steps, and surprising benefits of reproducible builds, based on Hervé’s insights and hands-on demonstrations.
What Are Reproducible Builds?
Reproducible builds ensure that compiling the same source code, with the same environment and build tools, produces identical binaries, byte-for-byte. This practice verifies that the distributed binary matches the source code, eliminating risks like malicious tampering or unintended changes. Hervé highlighted the infamous XZ incident, where discrepancies between source tarballs and Git repositories went unnoticed—reproducible builds could have caught this by ensuring the binary matched the expected source.
Originally pioneered by Linux distributions like Debian in 2013, reproducible builds have gained traction in the Java ecosystem. Hervé’s work has led to over 2,000 verified reproducible releases from 500+ open-source projects on Maven Central, with stats growing weekly.
Why Reproducible Builds Matter
Reproducible builds are primarily about security. They allow anyone to rebuild a project and confirm that the binary hasn’t been compromised (e.g., no backdoors or “foireux” additions, as Hervé humorously put it). But Hervé’s five-year experience revealed additional benefits:
- Build Validation: Ensure patches or modifications don’t introduce unintended changes. A “build successful” message doesn’t guarantee the binary is correct—reproducible builds do.
- Data Leak Prevention: Hervé found sensitive data (e.g., usernames, machine names, even a PGP passphrase!) embedded in Maven Central artifacts, exposing personal or organizational details.
- Enterprise Trust: When outsourcing development, reproducible builds verify that a vendor’s binary matches the provided source, saving time and reducing risk.
- Build Efficiency: Reproducible builds enable caching optimizations, improving build performance.
These benefits extend beyond security, making reproducible builds a powerful tool for developers, enterprises, and open-source communities.
Implementing Reproducible Builds with Maven
Hervé outlined a practical workflow to achieve reproducible builds, demonstrated through his open-source project, reproducible-central, which includes scripts and rebuild recipes for 3,500+ compilations across 627+ projects. Here’s how to make your Maven builds reproducible:
Step 1: Rebuild and Verify
Start by rebuilding a project from its source (e.g., a Git repository tag) and comparing the output binary to a reference (e.g., Maven Central or an internal repository). Hervé’s rebuild.sh
script automates this:
- Specify the Environment: Define the JDK (e.g., JDK 8 or 17), OS (Windows, Linux, FreeBSD), and Maven command (e.g.,
mvn clean verify -DskipTests
). - Use Docker: The script creates a Docker image with the exact environment (JDK, OS, Maven version) to ensure consistency.
- Compare Binaries: The script downloads the reference binary and checks if the rebuilt binary matches, reporting success or failure.
Hervé demonstrated this with the Maven Javadoc Plugin (version 3.5.0), showing a 100% reproducible build when the environment matched the original (e.g., JDK 8 on Windows).
Step 2: Diagnose Differences
If the binaries don’t match, use diffoscope
, a tool from the Linux reproducible builds community, to analyze differences. Diffoscope compares archives (e.g., JARs), nested archives, and even disassembles bytecode to pinpoint issues like:
- Timestamps: JARs include file timestamps, which vary by build time.
- File Order: ZIP-based JARs don’t guarantee consistent file ordering.
- Bytecode Variations: Different JDK major versions produce different bytecode, even for the same target (e.g., targeting Java 8 with JDK 17 vs. JDK 8).
- Permissions: File permissions (e.g., group write access) differ across environments.
Hervé showed a case where a build failed due to a JDK mismatch (JDK 11 vs. JDK 8), which diffoscope revealed through bytecode differences.
Step 3: Configure Maven for Reproducibility
To make builds reproducible, address common sources of “noise” in Maven projects:
- Fix Timestamps: Set a consistent timestamp using the
project.build.outputTimestamp
property, managed by the Maven Release or Versions plugins. This ensures JARs have identical timestamps across builds. - Upgrade Plugins: Many Maven plugins historically introduced variability (e.g., random timestamps or environment-specific data). Hervé contributed fixes to numerous plugins, and his
artifact:check-buildplan
goal identifies outdated plugins, suggesting upgrades to reproducible versions. - Avoid Non-Reproducible Outputs: Skip Javadoc generation (highly variable) and GPG signing (non-reproducible by design) during verification.
For example, Hervé explained that configuring project.build.outputTimestamp
and upgrading plugins eliminated timestamp and file-order issues in JARs, making builds reproducible.
Step 4: Test Locally
Before scaling, test reproducibility locally using mvn verify
(not install
, which pollutes the local repository). The artifact:compare
goal compares your build output to a reference binary (e.g., from Maven Central or an internal repository). For internal projects, specify your repository URL as a parameter.
To test without a remote repository, build twice locally: run mvn install
for the first build, then mvn verify
for the second, comparing the results. This catches issues like unfixed dates or environment-specific data.
Step 5: Scale and Report
For large-scale verification, adapt Hervé’s reproducible-central
scripts to your internal repository. These scripts generate reports with group IDs, artifact IDs, and reproducibility scores, helping track progress across releases. Hervé’s stats (e.g., 100% reproducibility for some projects, partial for others) provide a model for enterprise reporting.
Challenges and Lessons Learned
Hervé shared several challenges and insights from his journey:
- JDK Variability: Bytecode differs across major JDK versions, even for the same target. Always match the original JDK major version (e.g., JDK 8 for a Java 8 target).
- Environment Differences: Windows vs. Linux line endings (CRLF vs. LF) or file permissions (e.g., group write access) can break reproducibility. Docker ensures consistent environments.
- Plugin Issues: Older plugins introduced variability, but Hervé’s contributions have made modern versions reproducible.
- Unexpected Findings: Reproducible builds uncovered sensitive data in Maven Central artifacts, highlighting the need for careful build hygiene.
One surprising lesson came from file permissions: Hervé discovered that newer Linux distributions default to non-writable group permissions, unlike older ones, requiring adjustments to build recipes.
Interactive Learning: The Quiz
Hervé ended with a fun quiz to test the audience’s understanding, presenting rebuild results and asking, “Reproducible or not?” Examples included:
- Case 1: A Maven Javadoc Plugin 3.5.0 build matched the reference perfectly (reproducible).
- Case 2: A build showed bytecode differences due to a JDK mismatch (JDK 11 vs. JDK 8, not reproducible).
- Case 3: A build differed only in file permissions (group write access), fixable by adjusting the environment (reproducible with a corrected recipe).
The quiz reinforced a key point: reproducibility requires precise environment matching, but tools like diffoscope
make debugging straightforward.
Getting Started
Ready to make your Maven builds reproducible? Follow these steps:
- Clone reproducible-central and explore Hervé’s scripts and stats.
- Run
mvn artifact:check-buildplan
to identify and upgrade non-reproducible plugins. - Set
project.build.outputTimestamp
in your POM file to fix JAR timestamps. - Test locally with
mvn verify
andartifact:compare
, specifying your repository if needed. - Scale up using
rebuild.sh
and Docker for consistent environments, adapting to your internal repository.
Hervé encourages feedback to improve his tools, so if you hit issues, reach out via the project’s GitHub or Apache’s community channels.
Conclusion
Reproducible builds with Maven are not only achievable but transformative, offering security, trust, and operational benefits. Hervé Boutemy’s work demystifies the process, providing tools, scripts, and a clear roadmap to success. From preventing backdoors to catching configuration errors and sensitive data leaks, reproducible builds are a must-have for modern Java development.
Start small with artifact:check-buildplan
, test locally, and scale with reproducible-central
. As Hervé’s 3,500+ rebuilds show, the Java community is well on its way to making reproducibility the norm. Join the movement, and let’s build software we can trust!