GIDS Conference 2024 - Takeaways on 26th April
Overview
The Great International Developer Summit (GIDS) 2024 took place from 23rd to 26th April at IISc Bangalore, featuring over 150 tech talks with some well-known speakers. Sponsored by leading tech firms, including D.E. Shaw India, my workplace, the conference provided a fantastic learning experience. I had a blast learning new concepts and networking with the speakers when I attended on the 25th and 26th.
This blog post is a documentation of my takeaways from the talks on 26th April. You can find the 25th April’s post here.
All of the talks are now uploaded to SaltMarch. I’ve linked the corresponding videos under each talk.
Talks
Digital Modernization through accessibility - Scott Davis
Watch: https://developersummit.com/session/digital-modernization-through-accessibility
Scott’s keynote talk was a compelling argument to make accessibility a part of product design decisions. While the takeaways I’ve documented capture the essence of his presentation, they don’t fully convey the impact it had.
- Accessibility is more than just addressing disability; it’s about giving users different means of accessing technology. While we already interact with smartphones through sight, hearing, and touch, ture accessiblity means ensuring that everyone, regardless of their abilities, can use your product.
- Big Tech is radically re-defining how we interact with technologies. Some interesting examples are, user interactions on the Apple Vision Pro, and double tap gesture on Apple Watch 9. Most of Gen Z watches videos with subtitles or closed captions.
- Products should be designed with universal accessibility in mind. While the Web is inherently designed to be accessible, it’s often the practices of web developers that cripple it. When used correctly, HTML adheres to Web Content Accessiblity Guidelines (WCAG) 2.0 guidelines, which lays down the standards for web accessiblity. It’s necessary to give users options to engage with content using all senses such as subtitles, closed captions, and alt text.
- In addition to the accessibility guidelines, the web offers APIs such as the Web Speech API and Screen Reader APIs, which can be integrated with a few lines of code. Despite the availability of such tools to create accessible products, there is still a need for a shift in mindset regarding accessibility.
Evolutionary Architectures - Rebecca Parsons
Watch: https://developersummit.com/session/evolutionary-architectures
This talk by Rebecca Parsons stressed the importance of evolutionary architectures, advocating for practices like evolutionary database design and contract testing to enable flexibility and incremental evolution of systems.
- System architectures need to be designed to handle unforeseen changes, which are common in the wild. Examples include the rise of iPhone in the 2000s, frequent shifts in security and regulatory policies, and the rapid evolution of language features.
- Evolutionary architecture supports guided, incremental change across multiple dimensions.
- Make incremental changes to a system to support an evolutionary change.
- Incremental changes should be guided by fitness functions that validate how well a system meets the requirements.
- Guided incremental changes can only be enabled by rigorous automation in the form of Continuous Delivery.
- Systems should be architected, developed, and tested for evolvability. Confident incremental changes accrue over time to sweeping evolutions of the software. Evolutionary architectures allow changes to specific parts of the system without disrupting others.
- Defer design decisions until the last responsible moment to ensure that you have all the necessary information. Don’t predict future evolutions.
- You can’t evolve your code if you don’t understand it. Make the system automated, testable and maintainable.
- Follow Postel’s Law - Be liberal in what you accept, and conservative in what you send. Accept user inputs in all possible ways so that the system can evolve in response to external changes.
Design Trade-offs in Modern Architectures - Venkat Subramaniam
Watch: https://developersummit.com/session/design-trade-offs-in-modern-architectures
This was an entertaining talk by Venkat highlighting how software architects and developers should approach non-functional requirements in software design.
- The requirements of any system are divided into functional and non-functional. Functional requirements are explicitly stated by the users as business needs, whereas non-functional requirements – such as security, scalability, and performance – are characteristics of the system that are implicitly understood to be necessary, often not directly specified by users.
- A system can’t fulfill all non-functional characteristics, as some may inherently conflict with each other. Developers must collaborate with business to determine what’s adequate for the problem. For instance, developers might choose to write imperative code in C++ to achieve high performance, but the complex codebase leads to poor maintainability in the long run, especially when optimal performance wasn’t a user requirement.
- There’s usually a misconception in the understanding of monoliths and microservices,
- Monoliths are often distributed systems with mutliple independent pieces (separate database, application, and UI services), but they require a single or synchronized deployment.
- Microservices support independent deployments at the cost of higher complexity and coordination. If you have microservices which need coordinated deployment, they are not microservices.
- Microservices are not required to be micro, but they are focussed on solving a defined subset of the domain.
- The job of a system architect is to evaluate and balance these design trade-offs. Think of non-functional requirements not as switches to turn on, but as dials that are interconnected.
- Work with users to understand what’s really adequate. Don’t ask them whether they need consistency, ask them what they need more: consistency or availability.
- Performance is never about speed, it’s about whether it’s adequate. Performance optimizations that are necessary in a missile guidance software are often overkill in a food ordering app.
- You can either get high scalability or high performance, but not both at a reasonable cost.
Design Principles for Better Frontend Code - Venkat Subramaniam
Watch: https://developersummit.com/session/design-principles-for-better-frontend-code
As we were approaching the end of the conference, Venkat’s talk covered high level principles and practices to write maintainable JavaScript code.
- Following are common software principles to follow for a good design, especially for frontend code,
- High cohesion and low coupling. Keep functionally related code tightly coupled, while ensuring that unrelated code remains loosely coupled.
- Single Responsibility Principle. Each piece of code should have a single reason to change. This is only possible with high cohesion.
- However, when these principles are used incorrectly, it can lead to unnecessary complexity.
- Some JavaScript pitfalls to be mindful of,
- Always use
===
instead of==
to prevent type coercion issues. - Prefer
const
orlet
overvar
to avoid potential code design problems caused by hoisting. - Minimize the use of global variables to prevent unintended side effects.
- Always use
- Functional programming style lets you understand what the code is doing by just reading what’s necessary. Imperative code gives you all the details, but requires that you read through the entire code to understand what it’s doing. For functional style to be effective, keep the code at a single level of abstraction. Anything at lower levels of abstraction should be delegated instead of implemented.
- Imperative style code:
1 2 3 4 5 6 7 8
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const result = 0; for (int i = 0; i < numbers.length; i++) { if ((numbers[i] % 2) === 0) { result += (numbers[i] * 2); } } console.log(result);
- Functional style code:
1 2 3 4 5 6
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const result = numbers .filter((item) => item % 2 === 0) .map((item) => item * 2) .reduce((acc, item) => acc + item); console.log(result);
- Function style code with single level of abstraction:
1 2 3 4 5 6 7
const isEven = (num) => num % 2 === 0; // Delegated the operations to separate functions. const double = (num) => num * 2; const add = (a, b) => a + b; const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const result = numbers.filter(isEven).map(double).reduce(add); console.log(result);
- Imperative style code:
Pattern Matching in Java - Venkat Subramaniam
Watch: https://developersummit.com/session/pattern-matching-in-java
This final talk of the day covers the different pattern matching features now available in Java. Since it was mostly driven by examples, I really recommend checking out the full video if you’re interested.
switch
in Java has been historically used with statements (and break), but now there’s support for switch expressions. Statements are inherently more error-prone and difficult to understand since they force mutation. Returning a value, in functional style, is better than mutating a return variable.- Switch with statements,
1 2 3 4 5 6 7 8 9 10 11 12 13
switch(score): { case 10: grade = "A"; break; case 9: grade = "A"; break; case 8: grade = "B"; break; default: grade = "F"; }
- Switch expressions,
1 2 3 4 5
var grade = switch(score) { case 10, 9 -> "A"; case 8 -> "B"; default -> "F"; }
- Switch with statements,
- When using switch expressions, the compiler ensures that the switch is handling all possible values. Prefer not adding a default case, so that when any new possible value is added (In case of say enums), the developer is forced to update the switch expression to handle it.
- Pattern matching with instanceof was an intermediate step to enable switch expressions on objects. Pattern matching is for data oriented programming, whereas Polymorphism is for object oriented programming.
1 2 3 4 5 6 7 8 9 10 11
// Pattern matching with instanceof if (input instanceof String str) { System.out.println("input is automatically casted to String! - " + str.length()); } // Switch expressions with pattern matching switch(input) { case Integer i -> System.out.println("input is an integer"); case Double d -> System.out.println("input is a double"); default -> System.out.println("doesn't matter"); }
- Finally, switch expressions really shine with pattern matching on records, especially with guarded pattern matching.
1 2 3 4 5 6 7 8 9 10 11
sealed interface Trade {} record Sell(String stock, int quantity) implements Trade {} record Buy(String stock, int quantity) implements Trade {} public String process(Trade trade) { switch(trade) { case Sell sell when sell.quantity() >= 5000 -> "Sell needs approval!"; case Sell sell -> "Selling " + sell.quantity() + " of " + sell.ticker(); case Buy(String t, int q) -> "Buying " + q + " of " + t; } }