Recent Posts
Archives

Archive for the ‘en-US’ Category

PostHeaderIcon JpaSystemException: A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance

Case:

Entity declaration:

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Foo> foos = Lists.newArrayList();

This block

            user.getFoos().clear();
// instantiate `foos`, eg: final List<Foo> foos = myService.createFoos(bla, bla);
            user.setFoos(foos);

generates this error:

org.springframework.orm.jpa.JpaSystemException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.github.lalou.jonathan.blabla.User.foos

 Fix:

Do not use setFoos() ; rather, after clearing, use addAll(). In other words, replace:

            user.getFoos().clear();
            user.setFoos(foos);

with

user.getFoos().clear(); user.getFoos().addAll(foos);

 

(copied to https://stackoverflow.com/questions/78858499/jpasystemexception-a-collection-with-cascade-all-delete-orphan-was-no-longer )

PostHeaderIcon [DotAI2024] DotAI 2024: Marcin Detyniecki – Navigating Bias Toward Equitable AI Outcomes

Marcin Detyniecki, Group Chief Data Scientist and Head of AI Research at AXA, probed the ethical frontiers of artificial intelligence at DotAI 2024. Steering AXA’s R&D toward fair, interpretable ML amid insurance’s high-stakes decisions, Detyniecki dissected algorithmic bias through predictive justice lenses. His exploration grappled with AI’s paradoxical promise: a “black box” oracle that, if harnessed judiciously, could forge impartial futures despite inherent opacity.

Unmasking Inherent Prejudices in Decision Engines

Detyniecki commenced with COMPAS, a U.S. recidivism predictor that flagged disproportionate risks for Black defendants, igniting bias debates. Yet, he challenged snap judgments: human intuitions, too, falter—his own unease at a “shady” visage mirroring the tool’s contested outputs. This duality reveals bias as endemic, not algorithmic anomaly; data mirrors societal skews, amplifying inequities unless confronted.

In insurance, parallels abound: pricing models risk entrenching disparities by correlating proxies like zip codes with peril, sidelining root causes. Detyniecki advocated reconstructing “sensitive variables”—demographics or vulnerabilities—within models to enforce equity, inverting the blind-justice archetype. Justice, he posited, demands vigilant oversight, not ignorance, to calibrate decisions across strata.

Fairness metrics proliferate—demographic parity, equalized odds—yet clash irreconcilably: precision for individuals versus solidarity in groups. Detyniecki’s Fairness Compass, an open GitHub toolkit, simulates trade-offs, logging rationales for transparency. This framework recasts metrics as tunable dials, enabling stakeholders to align algorithms with values, be it meritocracy or diversity.

Architecting Transparent Pathways to Just Applications

Detyniecki unveiled AXA’s causal architectures, embedding interventions to disentangle correlations from causations. By modeling “what-ifs”—altering features sans sensitive ties—models simulate equitable scenarios, outperforming ad-hoc debiasing. In hiring analogies, this yields top talent sans gender skew; in premiums, it mutualizes risks across cohorts, balancing acuity with solidarity.

Challenges persist: metric incompatibility demands philosophical reckoning, and sensitive data access invites misuse. Detyniecki urged guarded stewardship—reconstructing attributes internally to audit without exposure—ensuring AI amplifies equity, not erodes it.

Ultimately, Detyniecki affirmed AI’s redemptive arc: though veiled, its levers, when pulled ethically, illuminate fairer horizons. Trust, he concluded, bridges the chasm—humans guiding machines toward benevolence.

Links:

PostHeaderIcon [SpringIO2024] Continuations: The Magic Behind Virtual Threads in Java by Balkrishna Rawool @ Spring I/O 2024

At Spring I/O 2024 in Barcelona, Balkrishna Rawool, a software engineer at ING Bank, captivated attendees with an in-depth exploration of continuations, the underlying mechanism powering Java’s virtual threads. Introduced as a final feature in Java 21 under Project Loom, virtual threads promise unprecedented scalability for Java applications. Balkrishna’s session demystified how continuations enable this scalability by allowing programs to pause and resume execution, offering a deep dive into their mechanics and practical applications.

Understanding Virtual Threads

Virtual threads, a cornerstone of Project Loom, are lightweight user threads designed to enhance scalability in Java applications. Unlike platform threads, which map directly to operating system threads and are resource-intensive, virtual threads require minimal memory, enabling developers to create millions without significant overhead. Balkrishna illustrated this by comparing platform threads, often pooled due to their cost, to virtual threads, which are created and discarded as needed, avoiding pooling anti-patterns. He emphasized that virtual threads rely on platform threads—termed carrier threads—for execution, with a scheduler mounting and unmounting them dynamically. This mechanism ensures efficient CPU utilization, particularly in I/O-bound applications where threads spend considerable time waiting, thus boosting scalability.

The Power of Continuations

Continuations, the core focus of Balkrishna’s talk, are objects that represent a program’s current state or the “rest” of its computation. They allow developers to pause a program’s execution and resume it later, a capability critical to virtual threads’ efficiency. Using Java’s Continuation API, Balkrishna demonstrated how continuations pause execution via the yield method, transferring control back to the caller, and resume via the run method. He showcased this with a simple example where a continuation printed values, paused at specific points, and resumed, highlighting the manipulation of the call stack to achieve this control transfer. Although the Continuation API is not intended for direct application use, understanding it provides insight into virtual threads’ behavior and scalability.

Building a Generator with Continuations

To illustrate continuations’ versatility, Balkrishna implemented a generator—a data structure yielding values lazily—using only the Continuation API, eschewing Java’s streams or iterators. Generators are ideal for resource-intensive computations, producing values only when needed. In his demo, Balkrishna created a generator yielding strings (“a,” “b,” “c”) by defining a Source object to handle value yields and pauses via continuations. The generator paused after each yield, allowing consumers to iterate over values in a loop, demonstrating how continuations enable flexible control flow beyond virtual threads, applicable to constructs like coroutines or exception handling.

Crafting a Simple Virtual Thread

In the session’s climax, Balkrishna guided attendees through implementing a simplified virtual thread class using continuations. The custom virtual thread paused execution during blocking operations, freeing platform threads, and supported a many-to-many relationship with carrier threads. He introduced a scheduler to manage virtual threads on a fixed pool of platform threads, using a queue for first-in-first-out scheduling. A demo with thousands of virtual threads, each simulating blocking calls, outperformed an equivalent platform-thread implementation, underscoring virtual threads’ scalability. By leveraging scoped values and timers, Balkrishna ensured accurate thread identification and resumption, providing a clear, hands-on understanding of virtual threads’ mechanics.

Links:

PostHeaderIcon [PyConUS 2024] Demystifying Python Decorators: A Comprehensive Tutorial

At PyConUS2024, Reuven M. Lerner, an esteemed independent trainer and consultant from Lerner Consulting, presented an exhaustive tutorial titled “All About Decorators.” This session aimed to strip away the perceived complexity surrounding Python’s decorators, revealing their inherent power and versatility. Reuven’s approach was to guide attendees through the fundamental principles, practical applications, and advanced techniques of decorators, empowering developers to leverage this elegant feature for cleaner, more maintainable code. The tutorial offered a deep dive into what decorators are, their internal mechanics, how to construct them, and when to employ them effectively in various programming scenarios.

Functions as First-Class Citizens: The Foundation of Decorators

At the heart of Python’s decorator mechanism lies the concept of functions as first-class objects. Reuven Lerner began by elucidating this foundational principle, demonstrating how functions in Python are not merely blocks of code but entities that can be assigned to variables, passed as arguments to other functions, and returned as values from functions. This flexibility is pivotal, as it allows for the dynamic manipulation and extension of code behavior without altering the original function definition.

He illustrated this with simple examples, such as wrapping print statements with additional lines of text. Initially, this might involve manually calling a “wrapper” function that takes another function as an argument. This manual wrapping, while functional, quickly becomes cumbersome when applied repeatedly across numerous functions. Reuven showed how this initial approach, though verbose, laid the groundwork for understanding the more sophisticated decorator syntax. The ability to treat functions like any other data type in Python empowers developers to create highly modular and adaptable code structures, a cornerstone for building robust and scalable applications.

The Power of Closures: Functions Returning Functions

Building upon the concept of first-class functions, Reuven delved into the powerful notion of closures. A closure is a function that remembers the environment in which it was created, even after the outer function has finished executing. This is achieved when an inner function is defined within an outer function, and the outer function returns this inner function. The inner function retains access to the outer function’s local variables, forming a “closure” over that environment.

Lerner’s explanations made it clear that closures are a critical stepping stone to understanding how decorators work. The decorator pattern fundamentally relies on an outer function (the decorator) that takes a function as input, defines an inner “wrapper” function, and then returns this wrapper. This wrapper function “closes over” the original function and any variables from the decorator’s scope, allowing it to execute the original function while adding pre- or post-processing logic. This concept is essential for functions that need to maintain state or access context from their creation environment, paving the way for more sophisticated decorator implementations.

Implementing the Decorator Pattern Manually

Before introducing Python’s syntactic sugar for decorators, Reuven walked attendees through the manual implementation of the decorator pattern. This hands-on exercise was crucial for demystifying the @ syntax and showing precisely what happens under the hood. The manual approach involves explicitly defining a “decorator function” that accepts another function (the “decorated function”) as an argument. Inside the decorator function, a new “wrapper function” is defined. This wrapper function contains the additional logic to be executed before or after the decorated function, and it also calls the decorated function. Finally, the decorator function returns this wrapper.

The key step, as Reuven demonstrated, is then reassigning the original function’s name to the returned wrapper function. For instance, my_function = decorator(my_function). This reassignment effectively replaces the original my_function with the new, enhanced wrapper function, without changing how my_function is called elsewhere in the code. This explicit, step-by-step construction revealed the modularity and power of decorators, highlighting how they can seamlessly inject new behavior into existing functions while preserving their interfaces. Understanding this manual process is fundamental to debugging and truly mastering decorator usage.

Python’s Syntactic Sugar: The @ Operator

Once the manual mechanics of decorators were firmly established, Reuven introduced Python’s elegant and widely adopted @ syntax. This syntactic sugar simplifies the application of decorators significantly, making code more readable and concise. Instead of the explicit reassignment, my_function = decorator(my_function), the @ symbol allows developers to place the decorator name directly above the function definition:

@decorator
def my_function():
#### ...

Lerner emphasized that this @ notation is merely a convenience for the manual wrapping process discussed earlier. It performs the exact same operation of passing my_function to decorator and reassigning the result back to my_function. This clarity was vital, as many developers initially find the @ syntax magical. Reuven illustrated how this streamlined syntax enhances code readability, especially when multiple decorators are applied to a single function, or when creating custom decorators for specific tasks. The @ operator makes decorators a powerful and expressive tool in the Python developer’s toolkit, promoting a clean separation of concerns and encouraging reusable code patterns.

Practical Applications of Decorators

The tutorial progressed into a series of practical examples, showcasing the diverse utility of decorators in real-world scenarios. Reuven presented various use cases, from simple enhancements to more complex functionalities:

  • “Shouter” Decorator: A classic example where a decorator modifies the output of a function, perhaps by converting it to uppercase or adding exclamation marks. This demonstrates how decorators can alter the result returned by a function.
  • Timing Function Execution: A highly practical application involves using a decorator to measure the execution time of a function. This is invaluable for performance profiling and identifying bottlenecks in code. The decorator would record the start time, execute the function, record the end time, and then print the duration, all without cluttering the original function’s logic.
  • Input and Output Validation: Decorators can be used to enforce constraints on function arguments or to validate the return value. For instance, a decorator could ensure that a function only receives positive integers or that its output adheres to a specific format. This promotes data integrity and reduces errors.
  • Logging and Authentication: More advanced applications include decorators for logging function calls, handling authentication checks before a function executes, or implementing caching mechanisms to store and retrieve previously computed results.

Through these varied examples, Reuven underscored that decorators are not just an academic curiosity but a powerful tool for injecting cross-cutting concerns (like logging, timing, validation) into functions in a clean, non-intrusive manner. This approach adheres to the “separation of concerns” principle, making code more modular, readable, and easier to maintain.

Decorators with Arguments and Stacking Decorators

Reuven further expanded the attendees’ understanding by demonstrating how to create decorators that accept arguments. This adds another layer of flexibility, allowing decorators to be configured at the time of their application. To achieve this, an outer function is required that takes the decorator’s arguments and then returns the actual decorator function. This creates a triple-nested function structure, where the outermost function handles arguments, the middle function is the actual decorator that takes the decorated function, and the innermost function is the wrapper.

He also covered the concept of “stacking decorators,” where multiple decorators are applied to a single function. When decorators are stacked, they are executed from the bottom up (closest to the function definition) to the top (furthest from the function definition). Each decorator wraps the function that results from the application of the decorator below it. This allows for the sequential application of various functionalities to a single function, building up complex behaviors from smaller, modular units. Reuven carefully explained the order of execution and how the output of one decorator serves as the input for the next, providing a clear mental model for understanding chained decorator behavior.

Preserving Metadata with functools.wraps

A common side effect of using decorators is the loss of the decorated function’s original metadata, such as its name (__name__), docstring (__doc__), and module (__module__). When a decorator replaces the original function with its wrapper, the metadata of the wrapper function is what becomes visible. This can complicate debugging, introspection, and documentation.

Reuven introduced functools.wraps as the standard solution to this problem. functools.wraps is itself a decorator that can be applied to the wrapper function within your custom decorator. When used, it copies the relevant metadata from the original function to the wrapper function, effectively “wrapping” the metadata along with the code.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
##### ... decorator logic ...
        return func(*args, **kwargs)
    return wrapper

This simple yet crucial addition ensures that decorated functions retain their original identity and documentation, making them behave more like their undecorated counterparts. Reuven stressed the importance of using functools.wraps in all custom decorators to avoid unexpected behavior and maintain code clarity, a best practice for any Python developer working with decorators.

Extending Decorator Concepts: Classes as Decorators and Decorating Classes

Towards the end of the tutorial, Reuven touched upon more advanced decorator patterns, including the use of classes as decorators and the application of decorators to classes themselves.

  • Classes as Decorators: While functions are the most common way to define decorators, classes can also serve as decorators. This is achieved by implementing the __call__ method in the class, making instances of the class callable. The __init__ method typically takes the function to be decorated, and the __call__ method acts as the wrapper, executing the decorated function along with any additional logic. This approach can be useful for decorators that need to maintain complex state or have more intricate setup/teardown procedures.
  • Decorating Classes: Decorators can also be applied to classes, similar to how they are applied to functions. When a class is decorated, the decorator receives the class object itself as an argument. The decorator can then modify the class, for example, by adding new methods, altering existing ones, or registering the class in some way. This is often used in frameworks for tasks like dependency injection, ORM mapping, or automatically adding mixins.

Reuven’s discussion of these more advanced scenarios demonstrated the full breadth of decorator applicability, showcasing how this powerful feature can be adapted to various architectural patterns and design needs within Python programming. This segment provided a glimpse into how decorators extend beyond simple function wrapping to influence the structure and behavior of entire classes, offering a flexible mechanism for meta-programming.

Hashtags: #Python #Decorators #PyConUS2024 #Programming #SoftwareDevelopment #Functions #Closures #PythonTricks #CodeQuality #ReuvenMLerner #LernerConsulting #LernerPython

PostHeaderIcon Onyxia: A User-Centric Interface for Data Scientists in the Cloud Age

Watch the video

Introduction

The team from INSEE presents Onyxia, an open-source, Kubernetes-based platform designed to offer flexible, collaborative, and powerful cloud environments for data scientists.

Rethinking Data Science Infrastructure

Traditional local development faces issues like configuration divergence, data duplication, and limited compute resources. Onyxia solves these by offering isolated namespaces, integrated object storage, and a seamless user interface that abstracts Kubernetes and S3 complexities.

Versatile Deployment

With a few clicks, users can launch preconfigured environments — including Jupyter notebooks, VS Code, Postgres, and MLflow — empowering fast innovation without heavy IT overhead. Organizations can extend Onyxia by adding custom services, ensuring future-proof, evolvable data labs.

Success Stories

Adopted across French universities and research labs, Onyxia enables students and professionals alike to work in secure, scalable, and fully-featured environments without managing infrastructure manually.

Conclusion

Onyxia democratizes access to powerful cloud tools for data scientists, streamlining collaboration and fostering innovation.

PostHeaderIcon [NDCOslo2024] Reusable Ideas About the Reuse of Software – Audun Fauchald Strand & Trond Arve Wasskog

In the sprawling digital expanse of Norway’s welfare agency, NAV, where 143 million lines of code burgeon, Audun Fauchald Strand and Trond Arve Wasskog, principal engineers, confront the Sisyphean challenge of maintenance. Their discourse, a clarion call for strategic reuse, dissects NAV’s labyrinthine codebase, advocating for shared components to curb redundancy. With a nod to domain-driven design and Conway’s Law, Audun and Trond weave a narrative of organizational alignment, technical finesse, and cultural recalibration, urging a shift from ad-hoc replication to deliberate commonality.

NAV, serving Norway’s social safety net, grapples with legacy sprawl. Audun and Trond, seasoned navigators of this terrain, challenge the mantra “reuse should be discovered, not designed.” Their thesis: intentional reuse, underpinned by product thinking, demands ownership, incentives, and architecture harmonized with organizational contours. From open-source libraries to shared services, they map a spectrum of reuse, balancing technical feasibility with social dynamics.

Redefining Reuse: From Code to Culture

Reuse begins with understanding context. Audun outlines NAV’s scale: thousands of developers, hundreds of teams, and a codebase ballooning through modernization. Copy-pasting code—tempting for speed—breeds technical debt. Instead, they champion shared libraries and services, like payment gateways or journaling systems, already reused across NAV’s ecosystem. Open-source, they note, exemplifies external success; internally, however, reuse falters without clear ownership.

Trond delves into Conway’s Law: systems mirror organizational structures. NAV’s fragmented teams spawn siloed solutions unless guided by unified governance. Their solution: designate component owners, aligning incentives to prioritize maintenance over novelty. A payment service, reused across domains, exemplifies success, reducing duplication while enhancing reliability.

Technical Tactics and Organizational Orchestration

Technically, reuse demands robust infrastructure. Audun advocates platforms—centralized APIs, standardized pipelines—to streamline integration. Shared libraries, versioned meticulously, prevent divergence, while microservices enable modular reuse. Yet, technical prowess alone suffices not; social engineering is paramount. Trond emphasizes cross-team collaboration, ensuring components like letter-sending services are maintained by dedicated squads, not orphaned.

Their lesson: reuse is a socio-technical dance. Without organizational buy-in—financing, accountability, clear roles—components decay. NAV’s pivot to product-oriented teams, guided by domain-driven design, fosters reusable assets, aligning technical solutions with business imperatives.

Navigating Pitfalls: Ownership and Maintenance

The core challenge lies in the “blue box”—NAV’s monolithic systems. Audun and Trond dissect failures: reused components falter when unowned, leading to outages or obsolescence. Their antidote: explicit ownership models, where teams steward components, supported by funding and metrics. They cite successes—journaling services, payment APIs—where ownership ensures longevity.

Their vision: an internal open-source ethos, where teams contribute to and consume shared assets, mirrored by external triumphs like Kubernetes. By realigning incentives, NAV aims to transform reuse from serendipity to strategy, reducing code bloat while accelerating delivery.

Fostering a Reuse-First Mindset

Audun and Trond conclude with a cultural clarion: reuse thrives on intentionality. Teams must evaluate trade-offs—forking versus libraries, services versus platforms—within their context. Their call to action: join NAV’s mission, where reuse reshapes welfare delivery, blending technical rigor with societal impact.

Links:

PostHeaderIcon 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 like group: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" or strategy 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. 🌟

PostHeaderIcon [DevoxxUK2024] Project Leyden: Capturing Lightning in a Bottle by Per Minborg

Per Minborg, a seasoned member of Oracle’s Core Library team, delivered an insightful session at DevoxxUK2024, unveiling the ambitions of Project Leyden, a transformative initiative to enhance Java application performance. Focused on slashing startup time, accelerating warmup, and reducing memory footprint, Per’s talk explores how Java can evolve to meet modern demands while preserving its dynamic nature. By strategically shifting computations to optimize execution, Project Leyden introduces innovative techniques like condensers and enhanced Class Data Sharing (CDS). This session provides a roadmap for developers seeking to harness Java’s potential in high-performance environments, balancing flexibility with efficiency.

The Vision of Project Leyden

Per begins by outlining the core objectives of Project Leyden: improving startup time, warmup time, and memory footprint. Startup time, the duration from launching an application to its first meaningful output (e.g., a “Hello World” or serving a web request), is critical for user experience. Warmup time, the period until an application reaches peak performance through JIT compilation, can hinder responsiveness in dynamic systems. Footprint, encompassing memory and storage use, impacts scalability, especially in cloud environments. Per emphasizes that the best approach is to eliminate unnecessary computations, but when that’s not feasible, shifting them temporally—either earlier to compile time or later to runtime—can yield significant gains. This philosophy underpins Leyden’s strategy to refine Java’s execution model.

Shifting Computations for Efficiency

A cornerstone of Project Leyden is the concept of temporal computation shifting. Per explains that Java’s dynamic nature—encompassing dynamic class loading, JIT compilation, and runtime optimizations—enables expressive programming but can inflate startup and warmup times. By moving computations to build time, such as through constant folding or ahead-of-time (AOT) compilation, Leyden reduces runtime overhead. Alternatively, lazy evaluation postpones non-critical tasks, streamlining startup. Per introduces condensers, a novel mechanism that transforms program representations by shifting computations earlier, adding metadata, or imposing constraints on dynamism. Condensers are composable, meaning-preserving, and selectable, allowing developers to tailor optimizations based on application needs. For instance, a condenser might precompile lambda expressions into bytecode at build time, slashing runtime costs.

Enhancing Class Data Sharing (CDS)

Per delves into Class Data Sharing (CDS), a long-standing Java feature that Project Leyden enhances to achieve dramatic performance boosts. CDS allows pre-initialized JDK classes to be stored in a file, bypassing costly class loading during startup. With CDS++, Leyden extends this to include application classes, compiled code, and resolved constant pool references. Per shares compelling benchmarks: a test compiling 100 small Java files achieved a 2x startup improvement, while an XML parsing workload saw an 8x boost. For the Spring Pet Clinic benchmark, Leyden’s optimizations, including early class loading and cached compiled code, yielded up to 4x faster startup. These gains stem from a training run approach, where a representative execution gathers profiling data to inform optimizations, ensuring compatibility across platforms.

Balancing Dynamism and Performance

Java’s dynamism—encompassing dynamic typing, class loading, and reflection—empowers developers but complicates optimization. Per proposes selective constraints to balance this trade-off. For example, developers can restrict dynamic class loading for specific modules, enabling aggressive optimizations without sacrificing Java’s flexibility. The stable value feature, initially part of Leyden but now a standalone JEP, allows delayed initialization of final fields while maintaining performance akin to compile-time constants. Per illustrates this with a Fibonacci computation example, where memoization using stable values drastically reduces recursive overhead. By offering a “mixer board” of concessions, Leyden empowers developers to fine-tune performance, ensuring compatibility and preserving program semantics across diverse use cases.

Links:

PostHeaderIcon [NodeCongress2024] Asynchronous Context Tracking in Modern JavaScript Runtimes: From `AsyncLocalStorage` to the `AsyncContext` Standard

Lecturer: James M Snell

James M Snell is a distinguished open-source contributor and software engineer, currently serving as a Principal Engineer on the Cloudflare Workers team. He is a long-standing core contributor to the Node.js Technical Steering Committee (TSC), where his technical leadership has been instrumental in modernizing Node.js’s networking stack, including the implementation of HTTP/2, the WHATWG URL implementation, and the QUIC protocol. Snell is also a key founder and participant in the WinterCG (Web-interoperable Runtimes Community Group), an effort dedicated to aligning standards across disparate JavaScript runtimes.

Abstract

This article provides an analytical deep dive into the concept and implementation of Asynchronous Context Tracking in JavaScript runtimes, focusing on Node.js’s existing AsyncLocalStorage (ALS) API and the proposed AsyncContext standard. It explains the critical problem of preserving request-specific contextual data (e.g., request IDs or transaction details) across asynchronous I/O boundaries in highly concurrent environments. The article details the technical methodology, which relies on Async Hooks and a Context Frame Stack, and discusses the implications of the TC-39 standardization effort to create a portable, globally accessible AsyncContext API across runtimes like Node.js, Cloudflare Workers, Deno, and Bun.

Context: The Challenge of Asynchronous Execution Flow

In a concurrent, non-blocking I/O model like Node.js, the execution of a single logical operation (e.g., handling one HTTP request) is typically fragmented across multiple asynchronous callbacks. The JavaScript engine often switches between different logical requests while waiting for I/O operations to complete, making it impossible to rely on simple global or thread-local variables for storing request-specific metadata. The challenge is ensuring that contextual information (such as a unique request identifier or security principal) is preserved and accessible to every segment of the logical operation’s flow, regardless of how many other concurrent operations interleave with it.

Methodology: Context Frames and Async Hooks

Asynchronous Context Tracking solves this by establishing a mechanism to associate a context frame (a logical map of key/value pairs) with the execution flow of an asynchronous operation.

  • The Role of Async Hooks: The foundation of this system is the Async Hook API (or its internal equivalent in other runtimes). The runtime uses these hooks to trace the lifecycle of asynchronous resources (e.g., timers, network requests). Every time an asynchronous operation is created or executed, the runtime utilizes the hooks to push and pop context frames onto a dedicated stack for that specific asynchronous flow.
  • The run and getStore/get Methods: The primary interface for managing context is the run method (available on both AsyncLocalStorage and AsyncContext). When a function is wrapped in store.run(value, callback), it initiates a new context frame containing that value, ensuring that all subsequent asynchronous operations originating from the callback have access to the frame. The getStore (ALS) or get (Async Context) method then accesses the value from the current frame on the stack.
  • Copy-on-Run Principle: Critically, the run method ensures that context is copied and isolated for the new frame. Modifying a context value within a run call does not affect the context of the calling function, preventing data leakage or corruption between concurrent requests.

The Evolution to AsyncContext and Interoperability

The AsyncLocalStorage API in Node.js, initially residing in node:async_hooks, has proven the utility of this model, leading to its adoption in other runtimes. The subsequent step is the standardization of AsyncContext by the TC-39 committee. The changes between the two APIs are minimal—primarily making the API a global object and renaming getStore to get—but the implications are profound. The standardization effort ensures that this crucial pattern for context propagation becomes portable and interoperable across the entire JavaScript ecosystem, benefiting Node.js, Cloudflare Workers, Deno, and Bun.

Links

Hashtags: #AsyncContext #AsyncLocalStorage #NodeJS #JavaScriptRuntimes #AsyncHooks #WinterCG #TC39

PostHeaderIcon [DevoxxGR2024] Small Steps Are the Fastest Way Forward: Navigating Chaos in Software Development

Sander Hoogendoorn, CTO at iBOOD, delivered an engaging and dynamic talk at Devoxx Greece 2024, addressing the challenges of software development in a rapidly changing world. Drawing from his extensive experience as a programmer, architect, and leader, Sander explored how organizations can overcome technical debt and the innovator’s dilemma by embracing continuous experimentation, small teams, and short delivery cycles. His narrative, peppered with real-world anecdotes, offered practical strategies for navigating complexity and fostering innovation in a post-agile landscape.

Understanding Technical Debt and Quality

Sander opened by tackling the elusive concept of software quality, contrasting it with tangible products like coffee or cars, where higher quality correlates with higher cost. In software, quality—encompassing maintainability, testability, and reliability—is harder to quantify and often lacks a direct price relationship. He introduced Ward Cunningham’s concept of technical debt, where initial shortcuts accelerate development but, if unaddressed, can cripple organizations. Sander shared an example from an insurance company with 18 million lines of COBOL and 12 million lines of Java, where outdated code and retiring developers created a maintenance nightmare. Similarly, at iBOOD, a patchwork of systems led to “technical death,” where maintenance consumed all resources, stifling innovation.

To mitigate technical debt, Sander advocated for continuous refactoring as part of daily work, rather than a separate task requiring approval. He emphasized finding a balance between quality and cost, tailored to the organization’s goals—whether building a quick mobile app or a long-lasting banking system.

The Innovator’s Dilemma and Continuous Renovation

Sander introduced the innovator’s dilemma, where successful products reach a saturation point, and new entrants with innovative technologies disrupt the market. He recounted his experience at a company that pioneered smart thermostats but failed to reinvent itself, leading to its acquisition and dissolution. To avoid this fate, organizations must operate in “continuous renovation mode,” maintaining existing systems while incrementally building new features. This approach, inspired by John Gall’s law—that complex systems evolve from simple, working ones—requires small, iterative steps rather than large-scale rebuilds.

At iBOOD, Sander implemented this by allocating 70% of resources to innovation and 30% to maintenance, ensuring the “shop stays open” while progressing toward strategic goals. He emphasized the importance of defining a clear “dot on the horizon,” such as iBOOD’s ambition to become Europe’s leading deal site, to guide these efforts.

Navigating Complexity with the Cynefin Framework

To navigate the chaotic and complex nature of modern software development, Sander introduced the Cynefin framework, which categorizes problems into clear, complicated, complex, and chaotic zones. Most software projects reside in the complex zone, where no best practices exist, and experimentation is essential. He cautioned against treating complex problems as complicated, citing failed attempts at iBOOD’s insurance client to rebuild systems from scratch. Instead, organizations should run small experiments, accepting the risk of failure as a path to learning.

Sander illustrated this with iBOOD’s decision-making process, where a cross-functional team evaluates ideas based on their alignment with strategic goals, feasibility, and size. Ideas too large are broken into smaller pieces, ensuring manageable experiments that deliver quick feedback.

Delivering Features in Short Cycles

Sander argued that traditional project-based approaches and even Scrum’s sprint model are outdated in a world demanding rapid iteration. He advocated for continuous delivery, where features are deployed multiple times daily, minimizing dependencies and enabling immediate feedback. At iBOOD, features are released in basic versions, refined based on business input, and prioritized over less critical tasks. This approach, supported by automated CI/CD pipelines and extensive testing, ensures quality is built into the process, reducing reliance on manual inspections.

He shared iBOOD’s pipeline, which includes unit tests, static code analysis, and production testing, allowing developers to code with confidence. By breaking features into small, independent services, iBOOD achieves flexibility and resilience, avoiding the pitfalls of monolithic systems.

Empowering Autonomous Micro-Teams

Finally, Sander addressed the human element of software development, arguing that the team, not the individual, is the smallest unit of delivery. He advocated for autonomous “micro-teams” that self-organize around tasks, drawing an analogy to jazz ensembles where musicians form sub-groups based on skills. At iBOOD, developers choose their tasks and collaborators, fostering learning and flexibility. This autonomy, while initially uncomfortable for some, encourages ownership and innovation.

Sander emphasized minimizing rules to promote critical thinking, citing an Amsterdam experiment where removing traffic signs improved road safety through communication. By eliminating Scrum rituals like sprints and retrospectives, iBOOD’s teams focus on solving one problem daily, enhancing efficiency and morale.

Conclusion

Sander Hoogendoorn’s talk at Devoxx Greece 2024 offered a refreshing perspective on thriving in software development’s chaotic landscape. By addressing technical debt, embracing the innovator’s dilemma, and leveraging the Cynefin framework, organizations can navigate complexity through small, experimental steps. Continuous delivery and autonomous micro-teams further empower teams to innovate rapidly and sustainably. Sander’s practical insights, grounded in his leadership at iBOOD, provide a compelling blueprint for organizations seeking to evolve in a post-agile world.

Links: