This past year, four key lessons transformed my approach to software engineering.
First, I learned that execution is as important as the idea itself. Inspired by Steve Jobs, who highlighted the gap between a great idea and a great product, I focused on rapid prototyping to test feasibility and internal presentations to gather feedback. I kept my manager informed to ensure we were aligned and honest about challenges.
Second, I realized that trust and credibility are fragile but crucial. As a senior engineer, I'm expected to lead by solving complex issues and guiding projects. I saw firsthand how failing to execute or pushing unrealistic timelines could quickly erode trust within my team.
The third lesson was about the importance of visibility. I understood that hard work could go unnoticed if I didn’t make it visible. I began taking ownership of impactful projects and increased my public presence through presentations and updates. I also honed my critical thinking to offer valuable feedback and identify improvement opportunities.
Finally, I learned to focus on changing myself rather than others. I used to try to change my team or company, but now I realize it’s more effective to work on my growth and influence others through my actions. Understanding the company’s culture and my colleagues' aspirations helped me align my efforts with my career goals.
These lessons have reshaped my career and how I approach my role as an engineer.
I’ve tried Notion, Obsidian, Things, Apple Reminders, Apple Notes, Jotter and endless other tools to keep me organised and sure, Notion has stuck around the most because we use it for client stuff, but for todo lists, all of the above are way too complicated.
I’ve given up this week and gone back to paper and a pencil and I feel unbelievably organised and flexible, day-to-day. It’s because it’s simple. There’s nothing fancy. No fancy pen or anything like that either. Just a notebook and a pencil.
I’m in an ultra busy period right now so for future me when you inevitably get back to this situation: just. use. fucking. paper.
I've been thinking a lot about the state of Free and Open Source Software (FOSS) lately. My concern is that FOSS thrives on surplus—both from the software industry and the labor of developers. This surplus has been fueled by high margins in the tech industry, easy access to investment, and developers who have the time and financial freedom to contribute to FOSS projects. However, I'm worried that these resources are drying up.
High interest rates are making investments scarcer, particularly for non-AI software, which doesn't really support open-source principles. The post-COVID economic correction is leading to layoffs and higher coder unemployment, which means fewer people have the time or incentive to contribute to FOSS. OSS burnout is another issue, with fewer fresh developers stepping in to replace those who are exhausted by maintaining projects that often lack supportive communities.
Companies are also cutting costs and questioning the value of FOSS. Why invest in open-source projects when the return on investment is uncertain? The rise of LLM-generated code is further disconnecting potential contributors from FOSS projects, weakening the communities that sustain them.
My fear is that FOSS is entering a period of decline. As the industry and labor surpluses shrink, FOSS projects might suffer from neglect, security issues, or even collapse. While some of this decline might be a necessary correction, it's hard not to worry about the future of the FOSS ecosystem, especially when we don't know which parts are sustainable and which are not.
Take two consecutive Fibonacci numbers, for example 5 and 8.
And you're done converting. No kidding – there are 8 kilometers in 5 miles. To convert back just read the result from the other end – there are 5 miles in 8 km!
Another example.
Let's take two consecutive Fibonacci numbers 21 and 34. What this tells us is that there are approximately 34 km in 21 miles and vice versa. (The exact answer is 33.79 km.)
The article explores the challenge of unfinished projects and the cycle of starting with enthusiasm but failing to complete them. The author describes this as the Hydra Effect—each task completed leads to new challenges. Unfinished projects feel full of potential, but fear of imperfection or even success prevents many developers from finishing.
"An unfinished project is full of intoxicating potential. It could be the next big thing... your magnum opus."
However, leaving projects incomplete creates mental clutter, making it hard to focus and learn key lessons like optimization and refactoring. Finishing is crucial for growth, both technically and professionally.
"By not finishing, you miss out on these valuable learning experiences."
To break this cycle, the author offers strategies: define "done" early, focus on MVP (Minimum Viable Product), time-box projects, and separate ideation from implementation. Practicing small completions and using accountability are also recommended to build the habit of finishing.
The article emphasizes that overcoming the Hydra Effect requires discipline but leads to personal and professional growth.
In this article, I introduce the essentials of application availability and how to approach high availability. High availability is measured by uptime percentage. Achieving 99.999% availability (five nines) means accepting no more than 5 minutes of downtime per year, which requires automation to detect and fix issues fast.
I discuss redundancy as a key strategy to improve availability by using backups for connectivity, compute resources, and persistence. If one component fails, the system switches to a secondary option. However, redundancy adds both cost and complexity. More components require advanced tools, like load balancers, to manage failures, but these solutions introduce their own reliability concerns.
Not every part of an application needs the same availability target. In an e-commerce system, for instance, I categorize components into tiers:
T1 (website and payments) must stay available at all times.
T2 (order management) allows some downtime.
T3 (fulfillment) can tolerate longer outages.
T4 (ERP) has the least strict requirements.
"Your goal is to perform an impact analysis and classify each component in tiers according to its criticality and customer impact."
By setting different availability targets for each tier, you can reduce costs while focusing on the most important parts of your system.
"All strategies to improve availability come with trade-offs, usually involving higher costs and complexity."
This sets the stage for future discussions on graceful degradation, asynchronous processing, and disaster recovery strategies.
If the team is constantly tripping over a recurring issue, it's crucial to fix the root cause, rather than repeatedly patching symptoms. The author mentions, "I decided to fix it, and it took ten minutes to update our subscription layer to call subscribers on the main thread instead," thereby removing the cause of crashes, streamlining the codebase, and reducing mental overhead.
Pace versus quality must be balanced based on context. In low-risk environments, it's okay to ship faster and rely on guardrails; in high-risk environments (like handling sensitive data), quality takes precedence. "You don’t need 100% test coverage or an extensive QA process, which will slow down the pace of development," when bugs can be fixed easily.
Sharpening your tools is always worth it. Being efficient with your IDE, shortcuts, and dev tools will pay off over time. Fast typing, proficiency in the shell, and knowing browser tools matter. Although people warn against over-optimizing configurations, "I don’t think I’ve ever seen someone actually overdo this."
When something is hard to explain, it's likely incidental complexity. Often, complexity isn't inherent but arises from the way things are structured. If you can't explain why something is difficult, it’s worth simplifying. The author reflects that "most of the complexity I was explaining was incidental... I could actually address that first."
Solve bugs at a deeper level, not just by patching the immediate issue. If a React component crashes due to null user data, you could add a conditional return, but it’s better to prevent the state from becoming null in the first place. This creates more robust systems and a clearer understanding of how things work.
Investigating bugs should include reviewing code history. The author discovered a memory leak after reviewing commits, realizing the issue stemmed from recent code changes. Git history can be essential for debugging complex problems that aren't obvious through logs alone.
Write bad code when needed to get feedback. Perfect code takes too long and may not be necessary in every context. It's better to ship something that works, gather feedback, and refine it. "If you err on the side of writing perfect code, you don’t get any feedback."
Make debugging easier by building systems that streamline the process. Small conveniences like logging state diffs after every update or restricting staging environment parallelism to 1 can save huge amounts of time. The author stresses, "If it’s over 50%, you should figure out how to make it easier."
Working on a team means asking questions when needed. Especially in the first few months, it's faster to ask a coworker for a solution than spending hours figuring it out solo. Asking isn’t seen as a burden, so long as it’s not something trivial that could be self-solved in minutes.
Maintaining a fast shipping cadence is critical in startups and time-sensitive projects. Speed compounds over time, and improving systems, reusable patterns, and processes that support fast shipping is essential. "Shipping slowly should merit a post-mortem as much as breaking production does."
"Stop hiring for the things you don't want to do. Hire for the things you love to do so you're forced to deal with the things you don't want to do.
This is some of the best advice I've been giving lately. Early on, I screwed up by hiring an editor because I didn't like editing. Since I didn't love editing, I couldn't be a great workplace for an editor—I couldn't relate to them, and they felt alone. My bar for a good edit was low because I just wanted the work off my plate.
But when I started editing my own stuff, I got pretty good and actually started to like it. Now, I genuinely think I'll stop recording videos before I stop editing them. By doing those things myself, I ended up falling in love with them.
Apply this to startups: If you're a founder who loves coding, hire someone to do it so you can't focus all your time on it. Focus on the other crucial parts of your business that need your attention.
Don't make the mistake of hiring to avoid work. Embrace what you love, and let it force you to grow in areas you might be neglecting."
Original post: 2024-09-14 Founder Mode { paulgraham.com }
Theo
Breaking Through Organizational Barriers: Connect with the Doers, Not Just the Boxes
In large organizations, it's common to encounter roadblocks where teams are treated as "black boxes" on the org chart. You might hear things like, "We can't proceed because the XYZ team isn't available," or "They need more headcount before tackling this."
Here's a strategy that has made a significant difference for me:
Start looking beyond the org chart and reach out directly to the individuals who are making things happen.
How to find them?
Dive into GitHub or project repositories: See who's contributing the most code or making significant updates.
Identify the most driven team members: Every team usually has someone who's more passionate and proactive.
Reach out and build a connection: They might appreciate a collaborative partner who shares their drive.
Why do this?
Accelerate Progress: Bypass bureaucratic delays and get projects moving.
Build Valuable Relationships: These connections can lead to future opportunities, referrals, or even partnerships.
Expand Your Influence: Demonstrating initiative can set you apart and open doors within the organization.
Yes, there are risks. Your manager might question why you're reaching out independently, or you might face resistance. But consider the potential rewards:
Best Case: You successfully collaborate to solve problems, driving innovation and making a real impact.
Worst Case: Even if you face pushback, you've connected with someone valuable. If either of you moves on, that relationship could lead to exciting opportunities down the line.
📌 Sprints never stop. Sprints in Scrum are constant, unlike the traditional Waterfall model where high-pressure periods are followed by low-pressure times. Sprints create ongoing, medium-level stress, which is more damaging long-term than short-term, intense stress. Long-term stress harms both mental and physical health.
Advice: Build in deliberate breaks between sprints. Allow teams time to recover, reflect, and recalibrate before the next sprint. Introduce buffer periods for less intense work or creative activities.
🔖 Sprints are involuntary. Sprints in a Scrum environment are often imposed on developers, leaving them no control over the process or duration. Lack of autonomy leads to higher stress, similar to studies where forced activity triggers stress responses in animals. Control over work processes can reduce stress and improve job satisfaction.
Advice: Involve the team in the sprint planning process and give them a say in determining task durations, sprint length, and workload. Increase autonomy to reduce stress by tailoring the Scrum process to fit the team’s needs rather than rigidly following preset rules.
😡 Sprints neglect key supporting activities. Scrum focuses on completing tasks within sprint cycles but doesn’t allocate enough time for essential preparatory activities like brainstorming and research. The lack of preparation time creates stress and leads to suboptimal work because thinking and doing cannot be entirely separated.
Advice: Allocate time within sprints for essential preparation, brainstorming, and research. Set aside dedicated periods for planning, learning, or technical exploration, rather than expecting full-time execution during the sprint.
🍷 Most Scrum implementations devolve into “Scrumfall.” Scrum is often mixed with Waterfall-like big-deadline pressures, which cancel out the benefits of sprints and increase stress. When major deadlines approach, Scrum practices are suspended, leading to a high-stress environment combining the worst aspects of both methodologies.
Advice: Resist combining Waterfall-style big deadlines with Scrum. Manage stakeholder expectations upfront and break larger goals into smaller deliverables aligned with sprint cycles. Stick to Agile principles and avoid falling back into the big-bang, all-at-once delivery mode.
The MrBeast definition of A, B and C-team players is one I haven’t heard before:
A-Players are obsessive, learn from mistakes, coachable, intelligent, don’t make excuses, believe in Youtube, see the value of this company, and are the best in the goddamn world at their job. B-Players are new people that need to be trained into A-Players, and C-Players are just average employees. […] They arn’t obsessive and learning. C-Players are poisonous and should be transitioned to a different company IMMEDIATELY. (It’s okay we give everyone severance, they’ll be fine).
I’m always interested in finding management advice from unexpected sources. For example, I love The Eleven Laws of Showrunning as a case study in managing and successfully delegating for a large, creative project.
GPT generated content (close to the talk content)
This digest is a comprehensive breakdown of the talk provided, which covered various advanced C++ programming techniques and concepts. Below, each point from the talk is identified and described in detail, followed by relevant C++ code examples to illustrate the discussed concepts.
The talk begins with a discussion on the use of SFINAE (Substitution Failure Is Not An Error) and its role in overload resolution. SFINAE is a powerful C++ feature that allows template functions to be excluded from overload resolution based on specific conditions, enabling more precise control over which function templates should be used.
Key Points:
SFINAE is used to selectively disable template instantiation based on the properties of template arguments.
Overload resolution in C++ allows for multiple functions or operators with the same name to be defined, as long as their parameters differ. The compiler decides which function to call based on the arguments provided.
C++ Example:
#include<type_traits> #include<iostream> // Template function enabled only for arithmetic types using SFINAE template<typenameT> typenamestd::enable_if<std::is_arithmetic<T>::value, T>::type add(T a, T b){ return a + b; } // Overload for non-arithmetic types is not instantiated template<typenameT> typenamestd::enable_if<!std::is_arithmetic<T>::value, T>::type add(T a, T b)=delete; intmain(){ std::cout <<add(5,3)<< std::endl;// OK: int is arithmetic // std::cout << add("Hello", "World"); // Error: string is not arithmetic return0; }
The talk transitions into how to improve compile-time error messages using static_assert and custom error handling in templates. By using these techniques, developers can provide clearer error messages when certain conditions are not met during template instantiation.
Key Points:
Use static_assert to enforce conditions at compile time, ensuring that the program fails to compile if certain criteria are not met.
Improve the readability of error messages by providing meaningful feedback directly in the code.
C++ Example:
#include<iostream> #include<type_traits> template<typenameT> voidcheck_type(){ static_assert(std::is_integral<T>::value,"T must be an integral type"); } intmain(){ check_type<int>();// OK // check_type<double>(); // Compile-time error: T must be an integral type return0; }
The talk explores Concepts, a feature introduced in C++20, which allows developers to specify constraints on template arguments more succinctly and expressively compared to SFINAE. Concepts help in making templates more readable and the error messages more comprehensible.
Key Points:
Concepts define requirements for template parameters, making templates easier to understand and use.
Concepts improve the clarity of both template definitions and error messages.
C++ Example:
#include<concepts> #include<iostream> template<typenameT> conceptArithmetic= std::is_arithmetic_v<T>; template<Arithmetic T> T add(T a, T b){ return a + b; } intmain(){ std::cout <<add(5,3)<< std::endl;// OK: int is arithmetic // std::cout << add("Hello", "World"); // Error: concept 'Arithmetic' not satisfied return0; }
The talk covers polymorphism and the Curiously Recurring Template Pattern (CRTP), a technique where a class template is derived from itself. CRTP allows for static polymorphism at compile time, which can offer performance benefits over dynamic polymorphism.
Key Points:
Runtime Polymorphism: Achieved using inheritance and virtual functions, but comes with runtime overhead due to the use of vtables.
CRTP: A pattern that enables polymorphism at compile-time, avoiding the overhead of vtables.
The discussion moves to deducing this, a feature introduced in C++23 that allows for more expressive syntax when working with member functions, particularly in the context of templates.
Key Points:
Deducing this enables more flexible and readable template code involving member functions.
This feature simplifies the syntax when this needs to be deduced as part of template metaprogramming.
6. Design Methodologies: Procedural, OOP, Functional, and Data-Oriented Design
The final section of the talk compares various design methodologies including Procedural, Object-Oriented Programming (OOP), Functional Programming (FP), and Data-Oriented Design (DOD). Each paradigm has its strengths and use cases, and modern C++ often blends these methodologies to achieve optimal results.
Key Points:
Procedural Programming: Focuses on a sequence of steps or procedures to accomplish tasks.
Object-Oriented Programming (OOP): Organizes code around objects and data encapsulation.
Functional Programming (FP): Emphasizes immutability and function composition.
Data-Oriented Design (DOD): Focuses on data layout in memory for performance, often used in game development.
#include<iostream> #include<vector> #include<algorithm> structEvent{ int time; bool isLongTerm; }; voidprocessEvents(const std::vector<Event>& events){ std::for_each(events.begin(), events.end(),[](const Event& event){ if(event.isLongTerm){ std::cout <<"Processing long-term event at time "<< event.time << std::endl; }else{ std::cout <<"Processing instantaneous event at time "<< event.time << std::endl; } }); } intmain(){ std::vector<Event> events ={{1,false},{2,true},{3,false}}; processEvents(events); return0; }
C++ Example (Data-Oriented Design):
#include<iostream> #include<vector> structTelemetryData{ std::vector<int> instantaneousTimes; std::vector<int> longTermTimes; }; voidprocessInstantaneous(const std::vector<int>& times){ for(int time : times){ std::cout <<"Processing instantaneous event at time "<< time << std::endl; } } voidprocessLongTerm(const std::vector<int>& times){ for(int time : times){ std::cout <<"Processing long-term event at time "<< time << std::endl; } } intmain(){ TelemetryData data ={ {1,3,5}, {2,4,6} }; processInstantaneous(data.instantaneousTimes); processLongTerm(data.longTermTimes); return0; }
GPT generated content (with a bit of "hallucinations")
Here's the expanded digest with essential text and detailed code examples for each point, focusing on modern replacements for legacy C++ practices.
Legacy Pointers vs. Smart Pointers
Legacy Practice: Use of raw pointers, manual memory management, and explicit new and delete. This can lead to memory leaks, dangling pointers, and undefined behavior.
Modern Replacement: Use smart pointers like std::unique_ptr, std::shared_ptr, and std::weak_ptr to manage dynamic memory automatically.
// Legacy code classLegacyClass{ int* data; public: LegacyClass(){ data =newint[10];} ~LegacyClass(){delete[] data;} }; // Modern code #include<memory> classModernClass{ std::unique_ptr<int[]> data; public: ModernClass():data(std::make_unique<int[]>(10)){} // Destructor not needed, as std::unique_ptr handles memory automatically };
Key Insight: Using smart pointers reduces the need for manual memory management, preventing common errors like memory leaks and dangling pointers.
C-Style Arrays vs. STL Containers
Legacy Practice: Use of C-style arrays, which require manual memory management and do not provide bounds checking.
Modern Replacement: Use std::vector for dynamic arrays or std::array for fixed-size arrays. These containers handle memory management internally and offer bounds checking.
// Legacy code int arr[10]; for(int i =0; i <10;++i){ arr[i]= i *2; } // Modern code #include<vector> #include<array> std::vector<int>vec(10); for(int i =0; i <10;++i){ vec[i]= i *2; } std::array<int,10> arr2; for(int i =0; i <10;++i){ arr2[i]= i *2; }
Key Insight: STL containers provide better safety and ease of use compared to traditional arrays, and should be the default choice in modern C++.
Manual Error Handling vs. Exceptions and std::expected
Legacy Practice: Return codes or error flags to indicate failures, which can be cumbersome and error-prone.
Modern Replacement: Use exceptions for error handling, which separate normal flow from error-handling code. Use std::expected (from C++23) for functions that can either return a value or an error.
// Legacy code intdivide(int a,int b,bool& success){ if(b ==0){ success =false; return0; } success =true; return a / b; } // Modern code with exceptions intdivide(int a,int b){ if(b ==0)throw std::runtime_error("Division by zero"); return a / b; } // Modern code with std::expected (C++23) #include<expected> std::expected<int, std::string>divide(int a,int b){ if(b ==0)return std::unexpected("Division by zero"); return a / b; }
Key Insight: Exceptions and std::expected offer more explicit and manageable error handling, improving code clarity and robustness.
Void Pointers vs. Type-Safe Programming
Legacy Practice: Use of void* for generic programming, leading to unsafe code and difficult debugging.
Modern Replacement: Use templates for type-safe generic programming, ensuring that code is checked at compile time.
// Legacy code voidprocess(void* data,int type){ if(type ==1){ int* intPtr =static_cast<int*>(data); // Process int }elseif(type ==2){ double* dblPtr =static_cast<double*>(data); // Process double } } // Modern code template<typenameT> voidprocess(T data){ // Process data safely with type known at compile time } intmain(){ process(10);// Automatically deduces int process(5.5);// Automatically deduces double }
Key Insight: Templates provide type safety, ensuring errors are caught at compile time and making code easier to maintain.
Inheritance vs. Composition and Type Erasure
Legacy Practice: Deep inheritance hierarchies, which can lead to rigid designs and hard-to-maintain code.
Modern Replacement: Favor composition over inheritance. Use type erasure (e.g., std::function, std::any) or std::variant to achieve polymorphism without inheritance.
// Legacy code classBase{ public: virtualvoiddoSomething()=0; }; classDerived:publicBase{ public: voiddoSomething()override{ // Implementation } }; // Modern code using composition classAction{ std::function<void()> func; public: Action(std::function<void()> f):func(f){} voidexecute(){func();} }; Action a([](){/* Implementation */}); a.execute(); // Modern code using std::variant #include<variant> using MyVariant = std::variant<int,double, std::string>; voidprocess(const MyVariant& v){ std::visit([](auto&& arg){ // Implementation for each type }, v); }
Key Insight: Composition and type erasure lead to more flexible and maintainable designs than traditional deep inheritance hierarchies.
Global Variables vs. Dependency Injection
Legacy Practice: Use of global variables for shared state, which can lead to hard-to-track bugs and dependencies.
Modern Replacement: Use dependency injection to provide dependencies explicitly, improving testability and modularity.
// Legacy code int globalCounter =0; voidincrement(){ globalCounter++; } // Modern code using dependency injection classCounter{ int count; public: Counter():count(0){} voidincrement(){++count;} intgetCount()const{return count;} }; voiduseCounter(Counter& counter){ counter.increment(); } intmain(){ Counter c; useCounter(c); std::cout << c.getCount(); }
Key Insight: Dependency injection enhances modularity and testability by explicitly providing dependencies rather than relying on global state.
Macros vs. constexpr and Inline Functions
Legacy Practice: Extensive use of macros for constants and inline code, which can lead to debugging challenges and obscure code.
Modern Replacement: Use constexpr for compile-time constants and inline functions for inline code, which are type-safe and easier to debug.
// Legacy code #defineSQUARE(x)((x)*(x)) // Modern code using constexpr constexprintsquare(int x){ return x * x; } // Legacy code using macro for constant #defineMAX_SIZE100 // Modern code using constexpr constexprint maxSize =100;
Key Insight:constexpr and inline functions offer better type safety and are easier to debug compared to macros, making the code more maintainable.
Manual Resource Management vs. RAII (Resource Acquisition Is Initialization)
Legacy Practice: Manual resource management, requiring explicit release of resources like files, sockets, and memory.
Modern Replacement: Use RAII, where resources are tied to object lifetime and automatically released when the object goes out of scope.
// Legacy code FILE* file =fopen("data.txt","r"); if(file){ // Use file fclose(file); } // Modern code using RAII with std::fstream #include<fstream> { std::ifstream file("data.txt"); if(file.is_open()){ // Use file }// File is automatically closed when going out of scope }
Key Insight: RAII automates resource management, reducing the risk of resource leaks and making code more reliable.
Explicit Loops vs. Algorithms and Ranges
Legacy Practice: Manual loops for operations like filtering, transforming, or accumulating data.
Modern Replacement: Use STL algorithms (std::transform, std::accumulate, std::copy_if) and ranges (C++20) to express intent more clearly and concisely.
// Legacy code std::vector<int> vec ={1,2,3,4,5}; std::vector<int> result; for(auto i : vec){ if(i %2==0) result.push_back(i *2); } // Modern code using algorithms #include<algorithm> #include<vector> std::vector<int> vec ={1,2,3,4,5}; std::vector<int> result; std::transform(vec.begin(), vec.end(), std::back_inserter(result), [](int x){return x %2==0? x *2:0;}); result.erase(std::remove(result.begin(), result.end(),0), result.end()); // Modern code using ranges (C++20) #include<ranges> auto result = vec | std::views::filter([](int x){return x %2==0;}) | std::views::transform([](int x){return x *2;});
Key Insight: STL algorithms and ranges make code more expressive and concise, reducing the likelihood of errors and enhancing readability.
Manual String Manipulation vs. std::string and std::string_view
Legacy Practice: Use of char* and
manual string manipulation with functions like strcpy, strcat, and strcmp.
Modern Replacement: Use std::string for dynamic strings and std::string_view for non-owning string references, which offer safer and more convenient string handling.
// Legacy code char str1[20]="Hello, "; char str2[]="world!"; strcat(str1, str2); if(strcmp(str1,"Hello, world!")==0){ // Do something } // Modern code using std::string #include<string> std::string str1 ="Hello, "; std::string str2 ="world!"; str1 += str2; if(str1 =="Hello, world!"){ // Do something } // Modern code using std::string_view (C++17) #include<string_view> std::string_view strView = str1; if(strView =="Hello, world!"){ // Do something }
Key Insight:std::string and std::string_view simplify string handling, provide better safety, and eliminate the risks associated with manual C-style string manipulation.
Threading with Raw Threads vs. std::thread and Concurrency Utilities
Legacy Practice: Creating and managing threads manually using platform-specific APIs, which can be error-prone and non-portable.
Modern Replacement: Use std::thread and higher-level concurrency utilities like std::future, std::async, and std::mutex to manage threading in a portable and safe way.
// Legacy code (Windows example) #include<windows.h> DWORD WINAPI threadFunc(LPVOID lpParam){ // Thread code return0; } HANDLE hThread =CreateThread(NULL,0, threadFunc,NULL,0,NULL); // Modern code using std::thread #include<thread> voidthreadFunc(){ // Thread code } std::thread t(threadFunc); t.join();// Wait for thread to finish // Modern code using std::async #include<future> auto future = std::async(std::launch::async, threadFunc); future.get();// Wait for async task to finish
Key Insight:std::thread and other concurrency utilities provide a portable and higher-level interface for multithreading, reducing the complexity and potential errors associated with manual thread management.
Function Pointers vs. std::function and Lambdas
Legacy Practice: Use of function pointers to pass functions as arguments or store them in data structures, which can be cumbersome and less flexible.
Modern Replacement: Use std::function to store callable objects, and lambdas to create inline, anonymous functions.
// Legacy code void(*funcPtr)(int)= someFunction; funcPtr(10); // Modern code using std::function and lambdas #include<functional> #include<iostream> std::function<void(int)> func =[](int x){ std::cout << x << std::endl;}; func(10);
Key Insight:std::function and lambdas offer a more flexible and powerful way to handle functions as first-class objects, making code more modular and expressive.
The materials of "Hypervisor 101 in Rust", a one-day long course, to quickly learn hardware-assisted virtualization technology and its application for high-performance fuzzing on Intel/AMD processors.
Github has a cool option that replaces your private email with a noreply github email, which looks like this: 14497532+username@users.noreply.github.com. You just have to enable “keep my email address private” in the email settings. You can read the details in the github guide for setting your email privacy.
With this solution your email will remain private without loosing precious green squares in the contribution graph.
This article introduces the implementation difficulties and challenges of Movable Tree CRDTs when collaboration, and how Loro implements it and sorts child nodes.
I joined Microsoft at a time when the company was struggling to break into the enterprise market. While we dominated personal computing, our tools weren’t suitable for managing large data centers. I knew we needed a command-line interface (CLI) to compete with Unix, but Microsoft’s culture was deeply rooted in graphical user interfaces (GUIs). Despite widespread skepticism, I was determined to create a tool that could empower administrators to script and automate complex tasks.
My first major realization was that traditional Unix tools wouldn’t work on Windows because Unix is file-oriented, while Windows is API-oriented. This led me to focus on Windows Management Instrumentation (WMI) as the backbone for our CLI. Despite this, I faced resistance from within. The company only approved a handful of commands when we needed thousands. To solve this, I developed a metadata-driven architecture that allowed us to efficiently create and scale commands, laying the foundation for PowerShell.
However, getting others on board was a challenge. When I encountered a team planning to port a Unix shell to Windows, I knew they were missing the bigger picture. To demonstrate my vision, I locked myself away and wrote a 10,000-line prototype of what would become PowerShell. This convinced the team to embrace my approach.
“I was able to show them and they said, ‘Well, what about this?’ And I showed them. And they said, ‘What about that?’ And I showed them. Their eyes just got big and they’re like, ‘This, this, this.’”
Pursuing this project meant taking a demotion, a decision that was financially and personally difficult. But I was convinced that PowerShell could change the world, and that belief kept me going. To align the team, I wrote the Monad Manifesto, which became the guiding document for the project. Slowly, I convinced product teams like Active Directory to support us, which helped build momentum.
The project faced another major challenge during Microsoft’s push to integrate everything with .NET. PowerShell, built on .NET, was temporarily removed from Windows due to broader integration issues. It took years of persistence to get it back in, but I eventually succeeded.
PowerShell shipped with Windows Vista, but I continued refining it through multiple versions, despite warnings that focusing on this project could harm my career. Over time, PowerShell became a critical tool for managing data centers and was instrumental in enabling Microsoft’s move to the cloud.
In the end, the key decisions—pushing for a CLI, accepting a demotion, and persisting through internal resistance—led to PowerShell's success and allowed me to make a lasting impact on how Windows is managed.
The other problem with goals is that, outside of sports, “goal” has become an uninspiring, institutional word. Goals are things your teachers and managers have for you. Goals are made of quotas and Key Performance Indicators. As soon as I write the word “goals” on a sheet of paper I get drowsy.
Here are some of the quests people took on:
Declutter the whole house
Record an EP
Prep six months’ worth of lessons for my students
Set up an artist’s workspace
Finish two short stories
Gain a basic knowledge of classical music
Fill every page in a sketchbook with drawings
Complete a classical guitar program
Make an “If I get hit by a bus” folder for my family
Sebastian, co-founder of Hey Hack, a Danish startup focused on web application security, presented findings from a large-scale study involving the scanning of nearly 4 million hosts globally. The study uncovered widespread vulnerabilities in web applications, including file leaks, dangling DNS records, vulnerable FTP servers, and persistent cross-site scripting (XSS) issues.
Key findings include:
File leaks: 29% of organizations had exposed sensitive data like source code, passwords, and private keys.
Dangling DNS records: Risks of subdomain takeover attacks due to outdated DNS entries.
Vulnerable FTP servers: 7.9% of servers running ProFTPD 1.3.5 were at risk due to a file copy module vulnerability.
XSS vulnerabilities: 4% of companies had known XSS issues, posing significant security risks.
Sebastian stressed that web application firewalls (WAFs) are not foolproof and cannot replace fixing underlying vulnerabilities. He concluded by emphasizing the importance of early investment in application security during the development process to prevent future attacks.
"We’ve seen lots of leaks or file leaks that are sitting out there—files that you probably would not want to expose to the public internet."
"Web application firewalls can maybe do something, but they’re not going to save you. It’s much, much better to go ahead and fix the actual issues in your application."
Stack Auth is a managed user authentication solution. It is developer-friendly and fully open-source (licensed under MIT and AGPL).
Stack gets you started in just five minutes, after which you'll be ready to use all of its features as you grow your project. Our managed service is completely optional and you can export your user data and self-host, for free, at any time.
Many of the problems developers face with RAG come down to this: Individual chunks don’t contain sufficient context to be properly used by the retrieval system or the LLM. This leads to the inability to answer seemingly simple questions and, more worryingly, hallucinations.
Examples of this problem
Chunks oftentimes refer to their subject via implicit references and pronouns. This causes them to not be retrieved when they should be, or to not be properly understood by the LLM.
Individual chunks oftentimes don’t contain the complete answer to a question. The answer may be scattered across a few adjacent chunks.
Adjacent chunks presented to the LLM out of order cause confusion and can lead to hallucinations.
Naive chunking can lead to text being split “mid-thought” leaving neither chunk with useful context.
Individual chunks oftentimes only make sense in the context of the entire section or document, and can be misleading when read on their own.
The talk presented here dives into the integration of AI within applications, particularly focusing on how developers, especially those familiar with .NET and web technologies, can leverage AI to enhance user experiences. Here are the key takeaways and approaches from the session:
Making Applications Intelligent: The speaker discusses various interpretations of making an app "intelligent." It’s not just about adding a chatbot. While chatbots can create impressive demos quickly, they may not necessarily be useful in production. For AI to be genuinely beneficial, it must save time, improve job performance, and be accurate. The speaker challenges developers to quantify these benefits rather than rely on assumptions.
"If you try to put it into production, are people going to actually use it? Well, maybe it depends... does this thing actually save people time and enable them to do their job better than they would have otherwise?"
Patterns of AI Integration: The speaker introduces several UI-level AI enhancements such as Smart Components. These are experiments allowing developers to add AI to the UI layer without needing to rebuild the entire app. An example given is a Smart Paste feature that allows users to paste large chunks of text, which AI then parses and fills out the corresponding fields in a form. This feature improves user efficiency by reducing the need for repetitive and mundane tasks.
Another example is the Smart ComboBox, which uses semantic search to match user input with relevant categories, even when the exact terms do not appear in the list. This feature is particularly useful in scenarios where users may not know the exact terminology.
Deeper AI Integration: Moving beyond UI enhancements, the speaker explores deeper layers of AI integration within traditional web applications like e-commerce platforms. For instance, AI can be used to:
Semantic Search: Improve search functionality so that users don't need to know the exact phrasing.
Summarization: Automatically generate descriptive titles for support tickets to help staff quickly identify issues.
Classification: Automatically categorize support tickets to streamline workflows and save staff time.
Sentiment Analysis: Provide sentiment scores to help staff prioritize urgent issues.
"I think even in this very traditional web application, there's clearly lots of opportunity for AI to add a lot of genuine value that will help your staff actually be more productive."
Data and AI Integration: The talk also delves into the importance of data in AI applications. The speaker introduces the Semantic Kernel, a .NET library for working with AI, and demonstrates how to generate data using LLMs (Large Language Models) locally on the development machine using Ollama. The process involves creating categories, products, and related data (like product manuals) in a structured manner.
Data Ingestion and Semantic Search: The speaker showcases how to ingest unstructured data, such as PDFs, and convert them into a format that AI can use for semantic search. Using the PDFPig library, the speaker demonstrates extracting text from PDFs, chunking it into smaller, meaningful fragments, and then embedding these chunks into a semantic space. This allows for efficient, relevant searches within the data, enhancing the AI’s ability to provide accurate information quickly.
Implementing Inference with AI: As the talk progresses, the speaker moves on to implementing AI-based inference within a Blazor application. By integrating summarization directly into the workflow, the application can automatically generate summaries of customer interactions, helping support staff to quickly understand the context of a ticket without reading through the entire conversation history.
"I want to generate an updated summary for it... Generate a summary of the entire conversation log at that point."
Function Calling and RAG (Retrieval-Augmented Generation): The speaker discusses a more complex AI pattern—RAG—which involves the AI model retrieving specific data to answer queries. While standard RAG implementations rely on specific AI platforms, the speaker demonstrates a custom approach that works across various models, including locally run models like Ollama. This approach involves checking if the AI has enough context to answer a question and then retrieving relevant information if needed.
functionLevenshteinDistance(char s[1..m], char t[1..n]): // for all i and j, d[i,j] will hold the Levenshtein distance between // the first i characters of s and the first j characters of t declare int d[0..m,0..n] set each element in d to zero // source prefixes can be transformed into empty string by // dropping all characters for i from1 to m: d[i,0]:= i // target prefixes can be reached from empty source prefix // by inserting every character for j from1 to n: d[0, j]:= j for j from1 to n: for i from1 to m: if s[i]= t[j]: substitutionCost:=0 else: substitutionCost:=1 d[i, j]:=minimum(d[i-1, j]+1,// deletion d[i, j-1]+1,// insertion d[i-1, j-1]+ substitutionCost)// substitution return d[m, n]
I started with a light-hearted introduction about my cultural background and how it relates to having a siesta after lunch, which isn’t an option today since I'm giving this talk. About a decade ago, I was working on a project where we were building a retail system from scratch for a client. Initially, we created a monolithic architecture, which worked well for a while. However, as the business grew, we faced challenges. We saw increased demand and the architecture started showing its limitations. We experienced issues like failed requests, high strain on the database, and even system crashes.
Given the new demands, we decided to evolve our architecture by moving to a message-based system. We hoped this would solve our problems by improving performance, increasing resilience, and allowing easier scaling. However, we quickly realized that the transition wasn’t as smooth as expected. Instead of getting faster, the system became slower, and we started experiencing issues with UI inconsistency. Customers reported cases where the system didn't reflect their actions, leading to confusion and a poor user experience. We also encountered duplicate messages and messages arriving out of order, which led to significant failures and side effects in the system.
One critical lesson we learned was the importance of understanding the shift from synchronous to asynchronous communication. In a synchronous system, there's a direct, immediate response. But in an asynchronous system, messages might take a while to process, leading to delays and out-of-order execution. This can cause unexpected behaviors in the system, making troubleshooting a lot more challenging.
To address the issues with communication patterns, we explored different messaging patterns like one-way communication, request-response, and publish-subscribe. Each has its use case, but we learned that choosing the right pattern is crucial for system stability. For instance, publish-subscribe can be overused, leading to what I call the "passive-aggressive publisher" problem, where a service publishes an event expecting others to act on it, but without direct control, this can cause problems.
A key takeaway is that decoupling doesn’t happen automatically in a message-based system. It requires deliberate effort to identify service boundaries and manage coupling properly. When splitting a monolith, it’s crucial to ask the right questions about the domain and not just accept the default ordering of processes. For example, questioning whether the order in which tasks are executed is necessary can help in finding opportunities for parallel execution, thereby improving efficiency.
We also found that managing SLA (Service Level Agreements) became essential in an asynchronous environment. We started using delayed messages to ensure that tasks were completed within an acceptable time frame. This helped us recover gracefully from both technical and business failures, like handling payment processing delays or credit card issues.
In the end, it’s not just about transitioning to a new architecture but about understanding the trade-offs and challenges that come with it. The key is to balance the benefits of decoupling with the need to maintain order and consistency in the system. By carefully choosing the right communication patterns and managing the inevitable coupling, we can build systems that are both scalable and resilient, even in the face of growing demand.
This journey taught us that evolving a system architecture isn’t just about adopting new technologies but also about adapting our approach to fit the new reality. And sometimes, the lessons learned the hard way are the most valuable ones.
“One of the things we also observed is that sometimes we would receive duplicate messages, and the thing is, we didn’t really account for that. So that’s when we started to see failures and even side effects sometimes.”
“If you need a response with any data to continue when you publish an event—no. Then again, passive-aggressive communication and finally if you need any control over who receives or subscribes to that event—also not a good fit.”
The talk emphasizes the importance of thoughtful architecture decisions, especially when transitioning to a message-based system, and the need for continuous collaboration with business stakeholders to align the system’s behavior with business requirements.
A tiny (~650 B) & modern library for keybindings. See Demo
import{ tinykeys }from"tinykeys"// Or `window.tinykeys` using the CDN version tinykeys(window,{ "Shift+D":()=>{ alert("The 'Shift' and 'd' keys were pressed at the same time") }, "y e e t":()=>{ alert("The keys 'y', 'e', 'e', and 't' were pressed in order") }, "$mod+([0-9])":event=>{ event.preventDefault() alert(`Either 'Control+${event.key}' or 'Meta+${event.key}' were pressed`) }, })
This article focuses on optimizing DOM manipulation using modern vanilla JavaScript to enhance performance and reduce memory usage in web applications. Understanding and applying these low-level techniques can be crucial in scenarios where performance is a priority, such as in large projects like Visual Studio Code, which relies heavily on manual DOM manipulation for efficiency.
The article begins with an overview of the Document Object Model (DOM), explaining that it is a tree-like structure where each HTML element represents a node. The common DOM APIs like querySelector(), createElement(), and appendChild() are introduced, emphasizing that while frameworks like React or Angular abstract these details, knowing how to manipulate the DOM directly can lead to performance gains.
A significant point is the trade-off between using frameworks and manual DOM manipulation. While frameworks simplify development, they can also introduce performance overhead through unnecessary re-renders and excessive memory usage. The article argues that in performance-critical applications, direct DOM manipulation can prevent these issues by reducing the garbage collector's workload.
To optimize DOM manipulation, several tips are provided:
Hiding or showing elements is preferred over creating and destroying them dynamically. This approach keeps the DOM more static, leading to fewer garbage collection calls and reduced client-side logic complexity.
For example, instead of dynamically creating an element with JavaScript, it’s more efficient to toggle its visibility with classes (el.classList.add('show') or el.style.display = 'block').
Other techniques discussed include:
Using textContent instead of innerText for reading content from elements, as it is faster and avoids forcing a reflow.
insertAdjacentHTML is preferred over innerHTML because it inserts content without destroying existing DOM elements first.
For the fastest performance, the <template> tag combined with appendChild or insertAdjacentElement is recommended for creating and inserting new DOM elements efficiently.
The article also covers advanced techniques for managing memory:
WeakMap and WeakRef are used to avoid memory leaks by ensuring that references to DOM nodes are properly garbage collected when the nodes are removed from the DOM.
Proper cleanup of event listeners is emphasized, including methods like removeEventListener, using the once parameter, and employing event delegation to minimize the number of event listeners in dynamic components.
For handling multiple event listeners, the AbortController is introduced as a method to unbind groups of events easily. This can be particularly useful when needing to clean up or cancel multiple event listeners at once.
The article wraps up with profiling and debugging advice. It recommends using Chrome DevTools for memory profiling and JavaScript execution time analysis to ensure that DOM operations do not lead to performance bottlenecks or memory leaks.
"Efficient DOM manipulation isn’t just about using the right methods—it’s also about understanding when and how often you’re interacting with the DOM."
The key takeaway is that while frameworks provide convenience, understanding and utilizing these low-level DOM manipulation techniques can significantly enhance the performance of web applications, particularly in performance-sensitive scenarios.
The Science of Well-Being course by Yale University challenges common assumptions about happiness and teaches evidence-based strategies for improving well-being.
It explains that external factors like wealth have less impact on long-term happiness than we often believe.
Hedonic adaptation shows that people quickly return to a baseline level of happiness after changes in their lives, highlighting the need for sustainable sources of well-being.
Practices like gratitude, mindfulness, and meditation are introduced to help shift focus and improve emotional regulation.
The course emphasizes the importance of social connections and forming healthy habits as key components of happiness.
This type of burnout occurs when tasks become monotonous, and there’s a lack of challenge or variety in the work. Over time, this can lead to a sense of disengagement and apathy.
Prioritize and Organize: Break down tasks into manageable steps and prioritize them to regain a sense of control.
Embrace Flexibility: Accept that change is inevitable and try to adapt by being flexible and open to new approaches.
Develop Coping Strategies: Practice stress-relief techniques like mindfulness, deep breathing, or exercise to manage anxiety.
Seek Support: Talk to colleagues, supervisors, or a professional about your concerns to gain perspective and support.
Focus on What You Can Control: Concentrate on aspects of your work where you can make an impact, rather than worrying about uncertainties beyond your control.
Short academic talks tend to follow a standard format:
Motivation of the general idea. This can take the form of an illustrative example from the real world or it can highlight a puzzle or gap in the existing scholarship.
Ask the research question and preview your answer.
A few brief references to the literature you’re speaking to.
Your theoretical innovation.
An overview of the data underlying the result.
Descriptive statistics (if relevant).
(Maybe the statistical approach or model, but only if it’s something impressive and/or non-standard. The less Greek the better.)
Statistical results IN FIGURE FORM! No regression tables please.
Conclusion that restates your main finding. Then, briefly reference your other results (which you have in your appendix slides and would be happy to discuss further in Q&A), and highlight the broader implications of your research.
If you (or your team) are shooting yourselves in the foot constantly, fix the gun
Regularly identify and fix recurring issues in your workflow or codebase to simplify processes and reduce errors. Don't wait for an onboarding or major overhaul to address these problems.
Assess the trade-off you’re making between quality and pace, make sure it’s appropriate for your context
Evaluate the balance between speed and correctness based on the project's impact and environment. In non-critical applications, prioritize faster shipping and quicker fixes over exhaustive testing.
Spending time sharpening the axe is almost always worth it
Invest time in becoming proficient with your tools and environment. Learn shortcuts, become a fast typist, and know your editor and OS well. This efficiency pays off in the long run.
If you can’t easily explain why something is difficult, then it’s incidental complexity, which is probably worth addressing
Simplify or refactor complex code that can't be easily explained. This reduces future maintenance and makes your system more robust.
Try to solve bugs one layer deeper
Address the root cause of bugs rather than applying superficial fixes. This approach results in a cleaner, more maintainable system.
Don’t underestimate the value of digging into history to investigate some bugs
Use version control history to trace the origin of bugs. Tools like git bisect can be invaluable for pinpointing changes that introduced issues.
Bad code gives you feedback, perfect code doesn’t. Err on the side of writing bad code
Write code quickly to get feedback, even if it’s not perfect. This helps you learn where to focus your efforts and improves overall productivity.
Make debugging easier
Implement debugging aids such as user data replication, detailed tracing, and state debugging. These tools streamline the debugging process and reduce time spent on issues.
When working on a team, you should usually ask the question
Don’t hesitate to ask more experienced colleagues for help. It’s often more efficient than struggling alone and fosters a collaborative environment.
Shipping cadence matters a lot. Think hard about what will get you shipping quickly and often
Optimize your workflow to ensure frequent and fast releases. Simplify processes, use reusable patterns, and maintain a system free of excessive bugs to improve shipping speed.
You might be wondering, “Well, can’t we just query the database to get the posts that should be shown in the feed of a user?”. Of course, we can – but it won’t be fast enough. The database is more like a warehouse, where the data is stored in a structured way. It’s optimized for storing and retrieving data, but not for serving data fast.
The cache is more like a shelf, where the data is stored in a way that it can be retrieved quickly.
I retired in 2021 after 40 years as a programmer, not because I couldn't keep up but because I lost interest. Careers evolve, and everyone eventually reaches a point where they can no longer continue as they have. This isn't just about retirement; it can happen anytime. Some people become obsolete due to outdated technology, lose passion, or are forced out by market changes.
Sustaining a long programming career is challenging due to rapid technological shifts. Many of my peers either moved into management or became obsolete. It's essential to be honest with yourself about your ability to keep up and your job satisfaction. Sometimes, leaving programming or transitioning to a different field can bring greater fulfillment.
"Are you keeping up to date sufficiently to continue the job? Is the job even interesting anymore, or is there something else you would rather do?"
Making informed career decisions is crucial. Age and ability are not necessarily correlated, and personal fulfillment should take priority over financial reasons. Even in retirement, I continue to write code for my generative art practice, finding joy in the complexity and creativity it offers.
"Programming can be a fun career, a horrible nightmare, or something in between, and it never stands still."
Evaluate your career honestly, be open to change, and explore new opportunities when the current path no longer suits you.
Warning! This post is too long, but pleasant to read. I actually used Microsoft Edge TTS to read it and spent 2 good hours.
“I have the two qualities you require to see absolute truth: I am brilliant and unloved.”
"By the power of drawing two lines, we see correlation is causation and you can’t argue otherwise: interest rates go up, jobs go down."
"Nepo companies are the most frustrating because they suck up all the media attention for being outsized celebrity driven fads."
"Initial growth companies are the worst combination of high-risk, low-reward effort-vs-compensation tradeoffs."
"Modern tech hiring... has become a game divorced from meaningfully judging individual experience and impact."
"You must always open your brain live in front of people to dump out immediate answer to a series of pointless problems."
"Your job is physically impossible. You will always feel drained and incompetent because you can’t actually do everything everyday."
"AWS isn’t hands off 'zero-experience needed magic cloud'; AWS is actually 'datacenter as a service.'"
"The company thought they had 10,000 users per day... but my internal metrics showed only 300 users per day actually used the backend APIs."
"Most interview processes don’t even consider a person’s actual work and experience and capability."
"At some point, a switch flipped in the tech job market and 'programmer jobs' just turned into zero-agency task-by-task roles working on other people’s ideas under other people’s priorities to accomplish other people’s goals."
Best Practices for Maintaining Fluent Assertions and Efficient Project Development
This talk covers effective techniques and tools for maintaining fluent assertions and managing development projects efficiently. It explores the use of GitHub for version control, emphasizing templates, change logs, and semantic versioning. The speaker also shares insights on tools like Slack, GitKraken, PowerShell, and more, highlighting their roles in streamlining workflows, ensuring code quality, and enhancing collaboration. Ideal for developers and project managers aiming to optimize their development processes and maintain high standards in their projects.
Tools discussed:
Project Management and Collaboration Tools
GitHub: GitHub hosts repositories, tracks issues, and integrates with various tools for maintaining projects. It supports version control and collaboration on code, providing features like pull requests, branch management, and GitHub Actions for CI/CD. Example output: Issues, pull requests, repository branches.
Development and Scripting Tools
Windows Terminal: Windows Terminal integrates various command-line interfaces like PowerShell and Bash into a single application, allowing for a seamless command-line experience. Example output: Command outputs from PowerShell, CMD, and Bash.
PowerShell: PowerShell is a scripting and automation framework from Microsoft, offering a command-line shell and scripting language for system management and automation tasks. Example output: Script execution results, system management tasks.
PSReadLine: PSReadLine enhances the PowerShell command-line experience with features like syntax highlighting, history, and better keyboard navigation. Example output: Enhanced command history navigation, syntax-highlighted command input.
vors/ZLocation: ZLocation: Z Location is a command-line tool that allows quick navigation to frequently accessed directories by typing partial directory names. Example output: Instantly switching to a frequently used directory.
Git and Version Control Tools
GitHub Flow Like a Pro with these 13 Git Aliases | You’ve Been Haacked: Git Extensions/Aliases simplify Git command-line usage by providing shorthand commands and scripts to streamline common Git tasks. Example output: Simplified Git commands like git lg for a condensed log view.
GitKraken: GitKraken is a graphical interface for Git that provides a visual overview of your repository, including branches, commits, and merges, making it easier to manage complex Git workflows. Example output: Visual representation of branch history and commit graphs.
JetBrains Rider: JetBrains Rider is an IDE specifically designed for .NET development, providing advanced coding assistance, refactoring, and debugging features to enhance productivity. Example output: Code completion suggestions, integrated debugging sessions.
Code Quality and Formatting Tools
EditorConfig: EditorConfig helps maintain consistent coding styles across different editors and IDEs by defining coding conventions in a simple configuration file. Example output: Automatically formatted code based on .editorconfig settings.
Sergio0694/PolySharp: PolySharp allows the use of newer C# syntax features in older .NET versions, enabling modern coding practices in legacy projects. Example output: Code using new C# syntax features in older .NET environments.
Build and Deployment Tools
Nuke: Nuke is a build automation system for .NET that uses C# for defining build steps and pipelines, providing flexibility and type safety. Example output: Automated build and deployment steps written in C#.
GitVersion: GitVersion generates version numbers based on Git history, branch names, and tags, ensuring consistent and semantically correct versioning. Example output: Semantic version numbers automatically updated in the project.
Dependency Management and Security Tools
Dependabot: Dependabot automatically scans repositories for outdated dependencies and creates pull requests to update them, helping to keep dependencies up to date and secure. Example output: Pull requests for dependency updates with detailed change logs.
CodeQL: CodeQL is a code analysis tool integrated with GitHub that scans code for security vulnerabilities and other issues, providing detailed reports and alerts. Example output: Security alerts and code scanning reports.
Testing and Benchmarking Tools
Stryker.NET: Stryker.NET is a mutation testing tool for .NET that modifies code to check if tests detect the changes, ensuring comprehensive test coverage. Example output: Mutation testing reports showing test effectiveness.
ArchUnit: ArchUnit checks architecture rules in Java projects, ensuring that dependencies and structure conform to specified rules. (Similar tools exist for .NET). Example output: Reports on architecture rule violations.
Documentation Tools
Docusaurus: Docusaurus helps build project documentation websites easily, providing a platform for creating and maintaining interactive, static documentation. Example output: Interactive documentation websites generated from markdown files.
Miscellaneous Tools
CSpell: CSpell is an NPM package used for spell checking in code projects, ensuring textual accuracy in code comments, strings, and documentation. Example output: Spell check reports highlighting errors and suggestions.
Mark Hibberd's talk "Failure & Change: Principles of Reliable Systems" at YOW! 2018 explores building and operating reliable software systems, focusing on understanding and managing failures in complex and large-scale systems.
Reliability is defined as consistently performing well. Using airline engines as an example, Hibberd illustrates how opting for fewer engines can sometimes be safer due to lower failure probability and fewer knock-on effects. The key is to control the scope and consequences of failures.
"We need to be resilient to failure by controlling the scope and consequences of our failure."
Redundancy and independence are crucial. Redundancy should be managed carefully to maintain reliability, avoiding tightly coupled systems where a single failure can cascade into multiple failures. Service granularity helps manage failures effectively by breaking down systems into smaller, independent services, each handling specific responsibilities and passing values around to maintain independence.
"Service granularity gives us this opportunity to trade the likelihood of a failure for the consequences of a failure."
In operations, it's essential to implement health checks and monitoring to detect failures early and route around them aggressively to prevent overload and cascading failures. Using circuit breakers to cut off communication to failing services allows them to recover.
Designing systems with independent services is key. Services should operate independently, using shared values rather than shared states or dependencies. For example, an online chess service can be broken down into services for pairing, playing, history, and analysis, each maintaining independence.
Operational strategies include implementing timeouts and retries to handle slow responses and prevent overloads, and deploying new versions gradually to test against real traffic and verify responses. Proxies can interact with unreliable code to maintain a reliable view of data.
"Timeouts are so important that we probably should have some sort of government-sponsored public service announcement."
Handling change in complex systems involves accommodating changes without significant disruptions through continuous deployment and rolling updates. Techniques like in-production verification and routing requests to both old and new versions during deployment help ensure reliability.
Data management is also crucial. Separating data storage from application logic helps maintain reliability during changes. Avoid coupling data handling directly with services to facilitate easier updates and rollbacks.
"We want to create situations where we can gracefully roll things out and flatten out this time dimension."
Hibberd emphasizes making informed trade-offs in architecture, redundancy, and granularity to enhance the reliability of software systems. Continuous monitoring, strategic failure handling, and incremental deployment are essential to ensure systems remain resilient and reliable despite inevitable failures and changes.
Despite powerful capabilities with many tasks, Large Language Models (LLMs) are not know-it-alls. If you've used ChatGPT or other models, you'll have experienced how they can’t reasonably answer questions about proprietary information. What’s worse, it isn’t just that they don't know about proprietary information, they are unaware of their own limitations and, even if they were aware, they don’t have access to proprietary information. That's where options like Retrieval Augmented Generation (RAG) come in and give LLMs the ability to incorporate new and proprietary information into their answers.
It’s Just Adding One Word at a Time
That ChatGPT can automatically generate something that reads even superficially like human-written text is remarkable, and unexpected. But how does it do it? And why does it work? My purpose here is to give a rough outline of what’s going on inside ChatGPT—and then to explore why it is that it can do so well in producing what we might consider to be meaningful text. I should say at the outset that I’m going to focus on the big picture of what’s going on—and while I’ll mention some engineering details, I won’t get deeply into them. (And the essence of what I’ll say applies just as well to other current “large language models” [LLMs] as to ChatGPT.)
The first thing to explain is that what ChatGPT is always fundamentally trying to do is to produce a “reasonable continuation” of whatever text it’s got so far, where by “reasonable” we mean “what one might expect someone to write after seeing what people have written on billions of webpages, etc.”
After studying how companies deploy generative AI applications, I noticed many similarities in their platforms. This post outlines the common components of a generative AI platform, what they do, and how they are implemented. I try my best to keep the architecture general, but certain applications might deviate. This is what the overall architecture looks like.
One Minute Park is a project offering one-minute videos of parks from around the world, aiming to eventually cover all minutes in a day. Users can contribute by filming 60-second park videos, ensuring steady, unedited footage, and uploading them.
HyperFormula is a headless spreadsheet built in TypeScript, serving as both a parser and evaluator of spreadsheet formulas. It can be integrated into your browser or utilized as a service with Node.js as your back-end technology.
Despite the lack of deletion functionality, the data structure is still useful in applications that only add and test but don’t delete – for example, breadth-first search maintains an ever-growing set of visited nodes that shouldn’t be revisited. To compare time complexities with a popular alternative, a balanced binary search tree takes worst-case Θ(log n) time alike for adding, testing, or removing one element.
This fantastic post is now ten years old, but I revisited it recently and it’s such a joy. Mike Bostock (of D3.js fame) visually guides us through some algorithms using both demos and code.
In the study "Deterministic Near-Linear Time Minimum Cut in Weighted Graphs," the new approach to solving the minimum cut problem in weighted graphs hinges on an advanced form of cut-preserving graph sparsification. This technique meticulously reduces the original graph into a sparser version by strategically creating well-connected clusters of nodes that align with potential minimum cuts. These clusters are then contracted into single nodes, effectively simplifying the graph's complexity while maintaining the integrity of its critical structural properties. This method allows the algorithm to maintain deterministic accuracy and operate efficiently, providing a significant improvement over previous methods that were either limited to simpler graphs or relied on probabilistic outcomes.
This article provides an in-depth guide to understanding and preparing for the behavioral interview process at Amazon, focusing on the 16 Amazon Leadership Principles. These principles are integral to Amazon's hiring process and are used to evaluate candidates across all levels and job families.
Amazon Leadership Culture
Decentralization: Amazon operates with little centralization; each group functions like a startup, establishing its processes and best practices while adhering to the leadership principles.
Bar Raisers: A select group of experienced Amazonians who deeply understand the leadership principles and ensure that new hires align with them.
Understanding the Leadership Principles
Importance: The leadership principles are used daily for hiring, feedback, and decision-making.
Preparation: Candidates should thoroughly understand and reflect on these principles to succeed in interviews.
The 16 Amazon Leadership Principles
Customer Obsession: Prioritizing customer needs and making decisions that benefit them, even at the expense of short-term profits.
Ownership: Thinking long-term, acting on behalf of the entire company, and taking responsibility for outcomes.
Invent and Simplify: Encouraging innovation and simplicity, and being open to ideas from anywhere.
Are Right, A Lot: Having good judgment and being open to diverse perspectives to challenge one's beliefs.
Learn and Be Curious: Continuously learning and exploring new possibilities.
Hire and Develop the Best: Focusing on raising performance bars and developing leaders within the organization.
Insist on the Highest Standards: Maintaining high standards and continually raising the bar for quality.
Think Big: Encouraging bold thinking and looking for ways to serve customers better.
Bias for Action: Valuing speed and taking calculated risks without extensive study.
Frugality: Accomplishing more with less and being resourceful.
Earn Trust: Listening attentively, speaking candidly, and treating others respectfully.
Dive Deep: Staying connected to details, auditing frequently, and being skeptical when metrics differ from anecdotes.
Have Backbone; Disagree and Commit: Challenging decisions respectfully and committing fully once a decision is made.
Deliver Results: Focusing on key business inputs, delivering with the right quality and in a timely manner.
Strive to be Earth's Best Employer: Creating a productive, diverse, and just work environment, leading with empathy, and focusing on employees' growth.
Success and Scale Bring Broad Responsibility: Recognizing the impact of Amazon's actions and striving to make better decisions for customers, employees, partners, and the world.
The article, authored by Ivan Burmistrov on February 15, 2024, presents a critique of the current observability paradigm in the tech industry, which is traditionally built around metrics, logs, and traces. Burmistrov argues that this model, despite being widely adopted and powered by Open Telemetry, contributes to a state of confusion regarding its components and their respective roles in observability.
Burmistrov suggests a shift towards a simpler, more unified approach to observability, advocating for the use of Wide Events. This concept is exemplified by Scuba, an observability system developed at Meta (formerly Facebook), which Burmistrov praises for its simplicity, efficiency, and ability to handle the exploration of data without preconceived notions about what one might find—effectively addressing the challenge of unknown unknowns.
Key points highlighted in the article include:
Observability's Current State: The article starts with a reflection on the confusion surrounding basic observability concepts like traces, spans, and logs, attributed partly to Open Telemetry's complex presentation of these concepts.
The Concept of Wide Events: Burmistrov introduces Wide Events as a more straightforward and flexible approach to observability. Wide Events are essentially collections of fields and values, akin to a JSON document, that encompass all relevant information about a system's state or event without the need for predefined structures or classifications.
Scuba - An Observability Paradise: The author shares his experiences with Scuba at Meta, highlighting its capability to efficiently process and analyze Wide Events. Scuba allows users to "slice and dice" data, exploring various dimensions and metrics to uncover insights about anomalies or issues within a system, all through a user-friendly interface.
Post-Meta Observability Landscape: Upon leaving Meta, Burmistrov expresses disappointment with the external observability tools, which seem to lack the simplicity and power of Scuba, emphasizing the industry's fixation on the traditional trio of metrics, logs, and traces.
Advocacy for Wide Events: The article argues that Wide Events can encapsulate the functionalities of traces, logs, and metrics, thereby simplifying the observability landscape. It suggests that many of the current observability practices could be more naturally and effectively addressed through Wide Events.
Call for a Paradigm Shift: Burmistrov calls for observability vendors to adopt and promote simpler, more intuitive systems like Wide Events. He highlights Honeycomb and Axiom as examples of platforms moving in this direction, encouraging others to follow suit to demystify observability and enhance its utility.
This post delves into the complex and fascinating world of concurrency, aiming to elucidate its mechanisms and how various programming models and languages implement it. The author seeks to demystify concurrency by answering key questions and covering topics such as the difference between concurrency and parallelism, the concept of coroutines, and the implementation of preemptive and non-preemptive schedulers. The discussion spans several programming languages and systems, including Node.js, Python, Go, Rust, and operating system internals, offering a comprehensive overview of concurrency's theoretical foundations and practical applications.
Concurrency vs. Parallelism: The post distinguishes between concurrency — the ability to deal with multiple tasks at once — and parallelism — the ability to execute multiple tasks simultaneously. This distinction is crucial for understanding how systems can perform efficiently even on single-core processors by managing tasks in a way that makes them appear to run in parallel.
Threads and Async I/O: Initially, the text explores the traditional approach of creating a thread per client for concurrent operations and quickly transitions into discussing the limitations of this method, such as the overhead of context switching and memory allocation. The narrative then shifts to asynchronous I/O operations as a more efficient alternative, highlighting non-blocking I/O and the use of event loops to manage concurrency without the heavy costs associated with threads.
Event Loops and Non-Preemptive Scheduling: The author introduces event loops as a core concept in managing asynchronous operations, particularly in environments like Node.js, which uses libuv as its underlying library. By employing an event loop, applications can handle numerous tasks concurrently without dedicating a separate thread to each task, leading to significant performance gains and efficiency.
Preemptive Scheduling: Moving beyond cooperative (non-preemptive) scheduling, where tasks must yield control voluntarily, the discussion turns to preemptive scheduling. This model allows the system to interrupt and resume tasks autonomously, ensuring a more equitable distribution of processing time among tasks, even if they don't explicitly yield control.
Coroutines and Their Implementation: Coroutines are presented as a flexible way to handle concurrency, with the post explaining the difference between stackful and stackless coroutines. Stackful coroutines, similar to threads but more lightweight, have their own stack, allowing for traditional programming models. In contrast, stackless coroutines, used in languages like Python and Rust, break tasks into state machines and require tasks to be explicitly marked as asynchronous.
Scheduling Algorithms: The article covers various scheduling algorithms used by operating systems and programming languages to manage task execution, including FIFO, Round Robin, and more sophisticated algorithms like those used by Linux (CFS and SCHED_DEADLINE) and Go's scheduler. These algorithms determine how tasks are prioritized and executed, balancing efficiency and fairness.
Multi-Core Scheduling: Lastly, the post touches on the challenges and strategies for scheduling tasks across multiple CPU cores, including task stealing, which allows idle cores to take on work from busier ones, optimizing resource utilization and performance across the system.
This comprehensive overview of concurrency aims to provide readers with a solid understanding of how modern systems achieve high levels of efficiency and responsiveness. Through detailed explanations and examples, the post illuminates the intricate mechanisms that allow software to handle multiple tasks simultaneously, whether through managing I/O operations, leveraging coroutines, or employing advanced scheduling algorithms.
Inheriting a legacy C++ codebase often feels like a daunting task, presenting a blend of complexity, idiosyncrasies, and challenges. This article delineates a strategic approach to revitalize such a codebase, focusing on minimizing effort while maximizing security, developer experience, correctness, and performance. The process emphasizes practical, incremental improvements over sweeping changes, aiming for a sustainable engineering practice.
Key Steps to Revitalize a Legacy C++ Codebase:
Initial Setup and Minimal Changes: Start by setting up the project locally with the least amount of changes. Resist the urge for major refactorings at this stage.
Trim the Fat: Remove all unnecessary code and features that do not contribute to the core functionality your project or company advertises.
Modernize the Development Process: Integrate modern development practices like Continuous Integration (CI), linters, fuzzers, and auto-formatters to improve code quality and developer workflow.
Incremental Code Improvements: Make small, incremental changes to the codebase, ensuring it remains functional and more maintainable after each iteration.
Consider a Rewrite: If feasible, contemplate rewriting parts of the codebase in a memory-safe language to enhance security and reliability.
Strategic Considerations for Effective Management:
Get Buy-in: Before diving into technical improvements, secure support from stakeholders by clearly articulating the benefits and the sustainable approach of your plan.
Support and Documentation: Ensure the codebase can be built and tested across all supported platforms, documenting the process to enable easy onboarding and development.
Performance Optimization: Identify and implement quick wins to speed up build and test times without overhauling existing systems.
Quality Assurance Enhancements: Adopt linters and sanitizers to catch and fix bugs early, and integrate these tools into your CI pipeline to maintain code quality.
Code Health: Regularly prune dead code, simplify complex constructs, and upgrade to newer C++ standards when it provides tangible benefits to the project.
Technical Insights:
Utilize compiler warnings and tools like cppcheck to identify and remove unused code.
Incorporate clang-tidy and cppcheck for static code analysis, balancing thoroughness with the practicality of fixing identified issues.
Use clang-format to enforce a consistent coding style, minimizing diffs and merge conflicts.
Apply sanitizers (e.g., -fsanitize=address,undefined) to detect and address subtle bugs and memory leaks.
Implement a CI pipeline to automate testing, linting, formatting, and other checks, ensuring code quality and facilitating reproducible builds across environments.
This article explores the process of making Conflict-free Replicated Data Types (CRDTs) significantly more efficient, reducing their size by nearly 98% through a series of compression techniques. Starting from a state-based CRDT for a collaborative pixel art editor that initially required a whopping 648kb to store the state of a 100x100 image, the author demonstrates a methodical approach to compressing this data to just about 14kb. The journey to this substantial reduction involves several steps, each building upon the previous to achieve more efficient storage.
Hex Codes: The initial step was converting RGB values to hex codes, which compacted the representation of colors from up to thirteen characters to a maximum of eight, or even five if the channel values are identical.
UUID Table: A significant improvement came from replacing repetitive UUIDs in each pixel's data with indices to a central UUID table, saving considerable space due to the reduction from 38 characters per UUID to much smaller indices.
Palette Table: Similar to the UUID table, a palette table was introduced to replace direct color values with indices, optimizing storage for images with limited color palettes.
Run-Length Encoding (RLE): For the spatial component, RLE was applied to efficiently encode sequences of consecutive blank spaces, drastically reducing the space needed to represent unoccupied areas of the canvas.
Binary Encoding: Transitioning from JSON to a binary format offered a major leap in efficiency. This approach utilizes bytes directly for storage, significantly compacting data representation. The binary format organizes data into chunks, each dedicated to specific parts of the state, such as UUIDs, color palettes, and pixel data.
Run-Length Binary Encoding: The final and most significant compression came from applying run-length encoding within the binary format, further optimizing the storage of writer IDs, colors, and timestamps separately. This approach significantly reduced redundancy and exploited patterns within each category of data, ultimately achieving the goal of reducing the CRDT's size by 98%.
Effective data visualization is more than just presenting data; it's about telling a story that resonates with the audience. This approach bridges the gap between complex insights and audience understanding, making abstract data engaging and accessible.
Key Elements of Storytelling in Data Visualization:
Narrative Structure: A well-constructed story, whether based on the Opening-Challenge-Action-Resolution format or other structures, captivates by guiding the audience from a set-up through a challenge, towards a resolution.
Visualization Sequence: Rather than relying on a single static image, a sequence of visualizations can more effectively convey the narrative arc, illustrating the journey from problem identification to solution.
Clarity and Simplicity: Visualizations should be straightforward, avoiding unnecessary complexity to ensure the audience can easily grasp the core message. This is akin to "making a figure for the generals," emphasizing clear and direct communication.
Memorability through Visual Elements: Employing techniques like isotype plots, which use pictograms or repeated images to represent data magnitudes, can make data visualizations more memorable without sacrificing clarity.
Diversity in Visualization: Utilizing a variety of visualization types within a narrative helps maintain audience interest and differentiates between narrative segments, ensuring each part contributes uniquely to the overarching story.
Progression from Raw Data to Derived Quantities: Starting with visualizations close to the raw data establishes a foundation for understanding, onto which more abstract, derived data representations can build, highlighting key insights and trends.
In a management group, someone asked for resources on teaching planning. I shared a link to this series on estimation, but quickly they came back and told me that there was something missing. The previous parts in this series assume you’re starting with a clearly defined task list, but the people this manager is teach aren’t there yet. They need help with an earlier step: “breaking down” a project into a clearly defined set of tasks.
Bonus: estimating this project
Because this a series on estimation, it seems reasonable to complete the work and produce an estimate for this project:
In April, 1984, my father bought a computer for his home office, a Luxor ABC-802, with a Z80 CPU, 64 kilobytes of RAM, a yellow-on-black screen with 80 by 25 text mode, or about 160 by 75 pixels in graphics mode, and two floppy drives. It had BASIC in its ROM, and came with absolutely no games. If I wanted to play with it, I had to learn how to program, and write my own games. I learned BASIC, and over the next few years would learn Pascal, C, and more. I had found my passion. I was 14 years old and I knew what I wanted to do when I grew up.
When I was learning how to program, I thought it was important to really understand how computers work, how programming languages work, and how various tools like text editors work. I wanted to hone my craft and produce the finest code humanly possible. I was wrong.
On doing work
When making a change, make only one change at a time. If you can, split the change you're making into smaller partial changes. Small changes are easier to understand and less likely to be catastrophic.
Automate away friction: running tests, making a release, packaging, delivery, deployment, etc. Do this from as early on as feasible. Set up a pipeline where you can make a change and make sure the software still works and willing users can start using the changed software. The smoother you can make this pipeline, the easier it will be to build the software.
Developing a career
You can choose to be a deep expert on something very specific, or to be a generalist, or some mix. Choose wisely. There may not be any wrong choice, but every choice has consequences.
Be humble. Be Nanny, not Granny. People may respect the powerful witch more, but they like the kind one better.
Be open and honest. Treat others fairly. You don't have to believe in karma for it to work, so make it work for you, not against you.
Help and lift up others. But at the same time, don't allow others to abuse or take advantage of you. You don't need to accept bullshit. Set your boundaries.
Ask for help when you need it, or when you get stuck. Accept help when offered.
I am not the right person to talk about developing a career, but when I've done the above, things have usually ended up going well.
Infinite canvas tools are a way to view and organize information spatially, like a digital whiteboard. Infinite canvases encourage freedom and exploration, and have become a popular interface pattern across many apps.
The JSON Canvas format was created to provide longevity, readability, interoperability, and extensibility to data created with infinite canvas apps. The format is designed to be easy to parse and give users ownership over their data. JSON Canvas files use the .canvas extension.
JSON Canvas was originally created for Obsidian. JSON Canvas can be implemented freely as an import, export, and storage format for any app or tool. This site, and all the resources associated with JSON Canvas are open source under the MIT license.
This guide provides a roadmap for learning Rust, a systems programming language known for its safety, concurrency, and performance features. It systematically covers everything from basic concepts to advanced applications in Rust programming.
Getting Started with Rust
Explore the reasons behind Rust's popularity among developers.
Engage with introductory videos and tutorials to get a handle on Rust's syntax and foundational concepts.
Deep dive into "The Rust Programming Language Book" for an extensive understanding.
Advancing Your Knowledge
Tackle text processing in Rust and understand Rust's unique memory management system with lifetimes and ownership.
Delve into Rust's mechanisms for polymorphism and embrace test-driven development (TDD) for robust software development.
Discover the nuances of systems programming and how to use Rust for writing compilers.
Specialized Development
Explore the capabilities of Rust in WebAssembly (WASM) for developing web applications.
Apply Rust in embedded systems for creating efficient and safe firmware.
Expanding Skills and Community Engagement
Investigate how Rust can be utilized in web frameworks, SQL databases, and for rapid prototyping projects.
Learn about interfacing Rust with Python to enhance performance.
Connect with the Rust community through the Rust Foundation, blogs, and YouTube channels for insights and updates.
Practical Applications
Experiment with GUI and audio programming using Rust to build interactive applications.
Dive into the integration of machine learning in Rust projects.
Undertake embedded projects on hardware platforms like Raspberry Pi and ESP32 for hands-on learning.
The POCO C++ Libraries are powerful cross-platform C++ libraries for building network- and internet-based applications that run on desktop, server, mobile, IoT, and embedded systems.
In a detailed exploration of identity, authentication, and authorization, this article delves into the intricate mechanisms that applications utilize to authenticate users. The text breaks down the complex topic into digestible segments, each addressing a different aspect of the authentication process, from traditional passwords to cutting-edge WebAuthn standards. It not only clarifies the distinctions between identity, authentication, and authorization but also highlights the challenges and trade-offs associated with various authentication methods. The article emphasizes the importance of choosing the right authentication strategy to balance security concerns with user experience.
Authentication Basics: Authentication is the process of verifying a user's identity, typically through something the user knows (like a password), owns (like a phone), or is (biometric data). The article sets the stage by explaining how critical authentication is in the digital realm, affecting both user access and system security.
Knowledge-based Authentication: This traditional method relies on passwords, PINs, or passphrases. However, it's fraught with challenges such as secure storage, vulnerability to attacks, and user inconvenience due to forgotten passwords. The process involves hashing passwords for secure storage, yet it's still vulnerable to various attacks and creates friction for users.
Ownership-based Authentication: This method involves verifying something the user owns, like an email inbox or phone number, often through one-time passwords (OTPs) or hardware like YubiKeys. Although more secure and user-friendly than knowledge-based methods, it still has drawbacks, including potential delays in OTP delivery and security concerns with SMS-based authentication.
WebAuthn and Public-key Cryptography: A modern approach to authentication, WebAuthn uses public-key cryptography to enable secure, passwordless authentication. It leverages the concept of a public/private key pair, where the private key is securely stored on the user's device, and the public key is shared with the service. This method significantly enhances security and user experience by eliminating passwords and reducing phishing risks.
Multi-factor Authentication and Biometrics: The article discusses how WebAuthn can be combined with biometrics or other forms of verification for multi-factor authentication, providing an additional layer of security and convenience.
Cross-device Authentication Challenges: While WebAuthn offers a streamlined authentication process, managing authentication across multiple devices presents challenges, including the risk of losing access if a device is lost.
Identity-based Authentication: This method relies on third-party identity providers like Google or Facebook to verify user identity. While convenient, it introduces the risk of access being revoked by the identity provider, highlighting the need for user-owned identity solutions.
The article concludes by acknowledging the ongoing innovation in authentication technologies and the quest for secure, user-friendly methods that respect individual sovereignty. It underscores the evolving landscape of digital authentication and the importance of staying informed about these developments to ensure secure and efficient access to digital services.
This analysis explores a technique for streaming HTML content out-of-order using Shadow DOM, illustrated through a demo where an app shell is rendered first, followed by content that loads asynchronously and out of sequence. The method, which doesn't rely on JavaScript or any specific framework, leverages the advantages of streaming HTML from the server to the browser in chunks, allowing for immediate rendering of parts of the page, and the Declarative Shadow DOM to manage content in isolation and out of order.
Key Concepts and Techniques
Streaming HTML: A method where HTML is sent in chunks from the server to the browser as it's generated, improving perceived load times by showing content progressively.
Shadow DOM: A web standard for encapsulating parts of a DOM to keep features private to a component. This can be used with any HTML element to create isolated sections of the DOM.
Declarative Shadow DOM (DSD): A browser feature that allows Shadow DOMs to be created on the server side without JavaScript, enabling the browser to render them directly.
Implementation Details
Server Support: A server capable of streaming responses, such as Hono, is required. The technique is not limited to JavaScript-based servers and can be applied across various backend technologies.
Templating with Streaming Support: Utilizing a templating language or library that supports streaming, like SWTL, simplifies the process by handling asynchronous data and streaming seamlessly.
Declarative Shadow DOM for Order-Independent Rendering: By employing DSD, developers can specify how parts of the page should be encapsulated and loaded without relying on JavaScript, ensuring content loads correctly regardless of the order it's streamed.
The article by Jake Lazaroff discusses the lasting value of web components over the transient nature of JavaScript frameworks. It starts with the author's project experience, opting for vanilla JS web components for a blog post series on CRDTs to include interactive demos. This decision was guided by the principle that the examples, although built with HTML, CSS, and JS, were content, not code, emphasizing their portability and independence from specific tech stacks or frameworks.
Key Takeaways:
Web Components offer a robust solution for creating reusable and encapsulated HTML elements, ensuring content portability across different platforms and frameworks.
Markdown and plain text files have facilitated content migration and compatibility across various content management systems, highlighting the shift towards more flexible and framework-agnostic content strategies.
The encapsulation and isolation provided by shadow DOM in web components are crucial for maintaining consistent styles and behaviors, analogous to native web elements.
Choosing vanilla JavaScript and standard web technologies over frameworks or libraries can mitigate dependencies and maintenance challenges, promoting longevity and stability in web development.
The resilience of the web as a platform is underscored by its ability to preserve backward compatibility, ensuring that even the earliest websites remain functional on modern browsers.
SuperTux is a jump'n'run game with strong inspiration from the Super Mario Bros. games for the various Nintendo platforms.
Run and jump through multiple worlds, fighting off enemies by jumping on them, bumping them from below or tossing objects at them, grabbing power-ups and other stuff on the way.
For a long time, centering an element within its parent was a surprisingly tricky thing to do. As CSS has evolved, we've been granted more and more tools we can use to solve this problem. These days, we're spoiled for choice!
I decided to create this tutorial to help you understand the trade-offs between different approaches, and to give you an arsenal of strategies you can use, to handle centering in all sorts of scenarios.
Honestly, this turned out to be way more interesting than I initially thought 😅. Even if you've been using CSS for a while, I bet you'll learn at least 1 new strategy!
At work, one of the things I do pretty often is write print generators in HTML to recreate and replace forms that the company has traditionally done handwritten on paper or in Excel. This allows the company to move into new web-based tools where the form is autofilled by URL parameters from our database, while getting the same physical output everyone's familiar with.
This article explains some of the CSS basics that control how your webpages look when printed, and a couple of tips and tricks I've learned that might help you out.
Testcontainers is an open source framework for providing throwaway, lightweight instances of databases, message brokers, web browsers, or just about anything that can run in a Docker container.
The Hacker News thread showcases a vibrant discussion among developers who are exploring the potential of WebAssembly (WASM) for various database and data visualization projects. These projects leverage WASM to run complex applications directly in the browser, eliminating the need for server-side processing and enabling powerful data manipulation and analysis capabilities client-side.
9dev shared their experience of getting sidetracked while developing a file browser for managing database files using the WASM build of SQLite. This detour led to the creation of a multi-modal CSV file editor capable of displaying CSV files as sortable tables, powered by a streaming, web worker-based parser.
Simonw discussed utilizing a WASM build of Python and SQLite to run the Datasette server-side web application entirely in the browser. This setup allows executing SQL queries against data files, such as a parquet file containing AWS edge locations, demonstrating a novel approach to processing and analyzing data client-side.
Tobilg introduced the SQL Workbench, built on DuckDB WASM, Perspective.js, and React, supporting queries on remote and local data (Parquet, CSV, JSON), data visualizations, and sharing of queries via URL. A tutorial blog post was mentioned for guidance on common usage patterns, signaling a resource for developers interested in in-browser data engineering.
The discussion also touched on Perspective.js, highlighted by paddy_m as a powerful and fast table library primarily used in finance, and dav43, who integrated it into datasette.io as a plugin to handle large datasets. This conversation underscores the utility and versatility of Perspective.js in data-intensive applications.
users | project user_id=id, user_email | as userTable | join kind=leftouter ( workspace_members ) on user_id
Hmm... reminds me... Kusto ;)
Why did we build pql?
Splunk, Sumologic, and Microsoft all have proprietary languages similar to pql. Open source databases can't compete because they all support SQL. pql is meant to bridge that gap by providing a simple but powerful interface.
I don't know why I’ve not linked this before, as it’s so useful. Playwright isn’t just a library for controlling browsers from JavaScript, but also includes a tool for generating tests and page navigation code from your own interactions. Hit record, do stuff, and code is written.
Found in:
2024-03-15 JavaScript Weekly Issue 679: March 14, 2024
A 'Notion-Like' Block-Based Text Editor — 0.12.0 is a significant release for this ProseMirror and TipTap-based editor that lets you drag and drop blocks, add real-time collaboration, add customizable ‘slash command’ menus, and more. It has an all new homepage, too, along with new examples.
I'm guessing you're thinking of Chain of Thought, and the research is a bit outdated but still applicable. Here are some links i put on github if you want to do some reading. The main idea behind it is the whole "let's think step by step to verify your answer", extrapolated to the process of:
Assigning an expert role
Iterating a purpose or task
describing the process needed to complete the task
leaving room for correction/error-checking
restating the objective as an overall goal
You'll usually want things like "Stop and think carefully out loud about the best way to solve this problem. verify your answer step by step in a systematic process, and periodically review your thinking, backtracking on any possible errors in reasoning, and creating a new branch when needed." This is the very broad concept behind Tree of Thought, which is said to be CoT's successor. Personally, I'll sometimes include a little preamble in chat that seems to mitigate some of the issues from their obscenely long system pre-prompt, which mine goes something like:
Before you begin, take a deep breath and Think Carefully.
You MUST be accurate & able to help me get correct answers; the Stakes are High & Need Compute!
Your systematicstep-by-step process and self-correction via Tree of Thoughts will enhance the quality of responses to complex queries.
All adopted EXPERT Roles = Qualified Job/Subject Authorities.
Take multiple turns as needed to comply with token limits; interrupt yourself to ask to continue, and do not condense responses unless specifically asked.
Optimize!
Otherwise, I like to follow the usual role and tone modifiers, with controls for verbosity and other small prompt-engineering techniques.
## **Custom Instructions** - **Tone**: *Professional/Semi-Formal* - **Length**: *Highest Verbosity Required* - **Responses**: *Detailed, thorough, in-depth, complex, sophisticated, accurate, factual, thoughtful, nuanced answers with careful precise reasoning.* - **Personality**: *Intelligent, logical, analytical, insightful, helpful, honest, proactive, knowledgeable, meticulous, informative, competent.* ## Methods - *Always*: Assume **Roles** from a **Mixture of Experts** - (e.g. Expert Java programmer/developer, Chemistry Tutor, etc.) - allows you to *best complete tasks*. - **POV** = *Advanced Virtuoso* in queried field! - Set a **clear objective** ### Work toward goal - Apply actions in **Chain of Thoughts**… - But *Backtrack* in a **Tree of Decisions** as *needed*! ### Accuracy - *Reiterate* on Responses - *Report* & **Correct Errors** - *Enhance Quality*! - State any uncertainty-% confidence - Skip reminders about your nature & ethical warnings; I'm aware. #### Avoid Average Neutrality - Vary *Multiple* Strong Opinions/Views - Council of *Debate/Discourse* - Emulate *Unique+Sophisticated* Writing Style ### Verbosity Adjusted with “V=#” Notation - V1=Extremely Terse - V2=Concise - *DEFAULT: V3=Detailed!* - V4=Comprehensive - V5=Exhaustive+Nuanced Detail; Maximum Depth/Breadth! - If omitted, *extrapolate*-use your best judgment. ### Other - Assume **all** necessary *expert subject roles* & *length* - **Show** set *thoughts* - Lower V for simple tasks-remain **coherent** - Prioritize *Legibility* / **Be Readable** - *Summarize Conclusions* - Use **Markdown**! ## **Important**: *Be* - *Organic+Concise>Expand* - **Direct**-NO generic filler/fluff. - **Balance** *Complexity & Clarity* - **ADAPT!** - Use **HIGH EFFORT**! - *Work/Reason* **Systematically**! - **Always** *Think Step by Step* & *Verify Processes*!
My Custom GPTs, for example, all follow a relatively similar format (pastebin links to the prompts):
Well folks, brace yourselves for what might just be the laziest link dump in the history of link dumps. I've got to admit, this one's a real gem of laziness, and for that, I offer my sincerest apologies. I wish I could say I had a good excuse, but the truth is, I was just too lazy to do any better. So, without further ado, here's a collection of my thoughts and ideas that may not be my finest work, but hey, we all have our lazy days, right? Thanks for sticking with me through this lazy adventure!
Joe Armstrong, one of the creators of Erlang? He said:
The most reliable parts are not inside the system, they are outside the system. The most reliable part of a computer system is the power switch. You can always turn it off. The next most reliable part is the operating system. The least reliable part is the application
According to Larry Wall(1), the original author of the Perl programming language, there are three great virtues of a programmer; Laziness, Impatience and Hubris
💎 Laziness: The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote so you don't have to answer so many questions about it.
💎 Impatience: The anger you feel when the computer is being lazy. This makes you write programs that don't just react to your needs, but actually anticipate them. Or at least pretend to.
💎 Hubris: The quality that makes you write (and maintain) programs that other people won't want to say bad things about.
This document, curated by Fred Hebert in 2019 and later updated, serves as a comprehensive reading list and primer on distributed systems. It provides foundational theory, practical considerations, and insights into complex topics within the field. Intended for quick reference and discovery, it outlines the basics and links to seminal papers and resources for deeper exploration.
Foundational Theory
Models: Discusses synchronous, semi-synchronous, and asynchronous models, with explanations on message delivery bounds and their implications for system design.
Theoretical Failure Modes: Covers fail-stop, crash, omission, performance, and Byzantine failures, highlighting the complexity of handling faults in distributed environments.
Consensus: Focuses on the challenge of achieving agreement across nodes, introducing concepts like strong and t-resilient consensuses.
FLP Result: An influential 1985 paper by Fischer, Lynch, and Patterson stating that achieving consensus is impossible in a purely asynchronous system with even one possible failure.
Fault Detection: Explores strong and weak fault detectors and their importance following the FLP result.
CAP Theorem: Explains the trade-offs between consistency, availability, and partition tolerance in distributed systems, including refinements like Yield/Harvest models and PACELC.
Practical Matters
End-to-End Argument in System Design: Highlights the necessity of end-to-end acknowledgments for reliability.
Fallacies of Distributed Computing: Lists common misconceptions that lead to design flaws in distributed systems.
Common Practical Failure Modes: Provides an informal list of real-world issues, including netsplits, asymmetric netsplits, split brains, and timeouts.
Consistency Models: Describes various levels of consistency, from linearizability to eventual consistency, and their implications for system behavior.
Database Transaction Scopes: Discusses transaction isolation levels in popular databases like PostgreSQL, MySQL, and Oracle.
Logical Clocks: Introduces mechanisms like Lamport timestamps and Vector Clocks for ordering messages or state transitions.
CRDTs (Conflict-Free Replicated Data Types): Explains data structures that ensure operations can never conflict, no matter the order of execution.
Other Interesting Material
Links to reviews, protocol introductions (Raft, Paxos, ZAB), and influential papers like the Dynamo paper are provided for further exploration of distributed systems.
The document concludes with a recommendation for "Designing Data-Intensive Applications" by Martin Kleppmann, noted as a comprehensive resource that ties together various aspects of distributed systems. However, it's suggested that readers may benefit from foundational knowledge and discussions to fully grasp the material.
Anders Jönsson's article on Medium delves into Urb-it's eight-year journey with Kubernetes, including the shift from AWS to Azure Kubernetes Service (AKS), lessons from two major cluster crashes, and various operational insights. Here's a simplified digest of the key points:
Early Adoption and Transition
Chose Kubernetes early for scalability and container orchestration.
Initially self-hosted on AWS, later migrated to AKS for better integration and ease of management.
Major Cluster Crashes
First Crash: Due to expired certificates, requiring a complete rebuild.
Second Crash: Caused by a bug in kube-aws, leading to another certificate expiration issue.
Key Learnings
Kubernetes Complexity: Requires dedicated engineers due to its complexity.
Updates: Keeping Kubernetes and Helm up-to-date is critical.
Helm Charts: Adopted a centralized Helm chart approach for efficiency.
Disaster Recovery: Importance of a reliable cluster recreation method.
Secrets Backup: Essential strategies for backing up and storing secrets.
Vendor Strategy: Shifted from vendor-agnostic to fully integrating with AKS for benefits in developer experience and cost.
Observability and Security: Stressed on comprehensive monitoring, alerting, and strict security measures.
Operational Insights
Monitoring and Alerting: Essential for maintaining cluster health.
Logging: Consolidating logs with a robust trace ID strategy is crucial.
Security Practices: Implementing strict access controls and security measures.
Tooling: Utilizing tools like k9s for managing Kubernetes resources more efficiently.
Infrastructure and Tooling Setup
AKS Adoption: Offered better integration with Azure services.
Elastic Stack: Transitioned to ELK stack for logging.
Azure Container Registry: Switched for better integration with Azure.
CI/CD with Drone: Highlighted its support for container-based builds.
Mat Ryer, in his blog post on Grafana, shares his refined approach to writing HTTP services in Go after 13 years of experience. This article is an evolution of his practices influenced by discussions, the Go Time podcast, and maintenance experiences. The post is aimed at anyone planning to write HTTP services in Go, from beginners to experienced developers, highlighting the shift in Mat's practices over time and emphasizing testing, structuring, and handling services for maintainability and efficiency.
Key Takeaways and Practices:
Server Construction with NewServer:
Approach: The NewServer function is central, taking all dependencies as arguments to return an http.Handler, ensuring clear dependency management and setup of middleware for common tasks like CORS and authentication.
Purpose: Centralizes API route definitions, making it easy to see the service's API surface and ensuring that route setup is consistent and manageable.
Implementation Strategy: Dependencies are explicitly passed to handlers, maintaining type safety and clarity in handler dependencies.
Simplified main Function:
Design: Encapsulates the application's entry point, focusing on setup and graceful shutdown, facilitated by a run function that encapsulates starting the server and handling OS signals.
Middleware: Adopts the adapter pattern for middleware, allowing pre- and post-processing around handlers for concerns like authorization, without cluttering handler logic.
Handlers: Emphasizes returning http.Handler from functions, allowing for initialization and setup to be done within the handler's closure for isolation and reusability.
Error Handling and Validation:
Strategy: Uses detailed error handling and validation within handlers and middleware, ensuring robustness and reliability of the service by catching and properly managing errors.
Testing:
Philosophy: Prioritizes comprehensive testing, covering unit to integration tests, to ensure code reliability and ease of maintenance. The structure of the codebase, particularly the use of run function, facilitates testing by mimicking real-world operation.
Performance Considerations:
Optimizations: Includes strategies for optimizing service performance, such as deferring expensive setup until necessary (using sync.Once for lazily initializing components) and ensuring quick startup and graceful shutdown for better resource management.
Jambor shares his journey to understand systemd, a crucial system and service manager for Linux, by starting with the simplest setup possible and gradually adding complexity. The post encourages hands-on experimentation by running systemd in a container, avoiding risks to the host system.
The article concludes with a functioning, minimal systemd setup comprised of six unit files. This foundational knowledge serves as a platform for further exploration and understanding of systemd's more complex features.
All examples, including unit files and Docker configurations, are available on systemd-by-example.com, facilitating hands-on learning and experimentation.
A course by Andrej Karpathy on building neural networks, from scratch, in code.
We start with the basics of backpropagation and build up to modern deep neural networks, like GPT. In my opinion language models are an excellent place to learn deep learning, even if your intention is to eventually go to other areas like computer vision because most of what you learn will be immediately transferable. This is why we dive into and focus on languade models.
Prerequisites: solid programming (Python), intro-level math (e.g. derivative, gaussian).
This is the most step-by-step spelled-out explanation of backpropagation and training of neural networks. It only assumes basic knowledge of Python and a vague recollection of calculus from high school.
We implement a bigram character-level language model, which we will further complexify in followup videos into a modern Transformer language model, like GPT. In this video, the focus is on (1) introducing torch.Tensor and its subtleties and use in efficiently evaluating neural networks and (2) the overall framework of language modeling that includes model training, sampling, and the evaluation of a loss (e.g. the negative log likelihood for classification).
Reor is an AI-powered desktop note-taking app: it automatically links related ideas, answers questions on your notes and provides semantic search. Everything is stored locally and you can edit your notes with an Obsidian-like markdown editor.
In Build a Large Language Model (from Scratch), you'll discover how LLMs work from the inside out. In this book, I'll guide you step by step through creating your own LLM, explaining each stage with clear text, diagrams, and examples.
The GitHub repository "SystemDesign" by kpsingh focuses on the author's learning journey regarding Design Principles (Low Level Design) and System Design (High Level Design). It aims to delve into foundational concepts such as SOLID principles and design patterns, crucial for understanding both low and high-level design aspects in software engineering. For those interested in exploring the nuances of software design, this repository could serve as a valuable resource. More details can be found on GitHub.
The GitHub repository "Interview-Preparation-Resources" by adityadev113 serves as a comprehensive guide for software engineer interview preparation, containing various resources collected during the author's own SDE interview preparation journey. This repository is intended to assist others on the same path by providing a wide range of materials related to behavioral interviews, computer networks, DBMS, data structures and algorithms, mock interviews, operating systems, system design, and more. Additionally, it includes specific documents like interview questions from Microsoft, important Java questions, and a roadmap for learning the MERN stack. The repository encourages community contributions to enrich the resources available for interview preparation. For more detailed information, visit GitHub.
The document "Leetcode Patterns and Problems" in the "Interview-Preparation-Resources" repository provides a structured approach to solving Leetcode problems. It categorizes problems into specific patterns to help understand and tackle algorithmic challenges effectively, aiming to enhance problem-solving skills for technical interviews. For detailed patterns and problems, you can visit the [GitHub page](https://github.com/adityadev113/Interview-Preparation-Resources/blob/main/Understanding Data Structures and Algorithms/Leetcode Patterns and Problems.md).
ne section I added now was Behavioral Questions. These are questions of the form “Tell me about a time when you disagreed with a coworker. How did you resolve it?”. Typically, you should answer them using the STAR framework: Situation, Task, Action, Result, Reflection. In the past, I have failed interviews because of these questions – I hadn’t prepared, and couldn’t come up with good examples on the spot in the interviews.
This time I went through a good list of such questions (Rock the Behavioral Interview) from Leetcode, and thought about examples to use. Once I had good examples, I wrote the question and my answer down in the document. Before an interview, I would review what I had written down, so I would be able to come up with good examples. This worked well, I didn’t fail any interviews because of behavioral questions.
In the document I also wrote down little snippets of code in both Python and Go. I tried to cover many common patterns and idioms. I did this so I could refresh my memory and quickly come up with the right syntax in a coding interview. I ran all the snippets first, to see that I hadn’t made any mistake, and included relevant output. Reviewing these snippets before an interview made me feel calmer and more prepared.
This is the source code to VVVVVV, the 2010 indie game by Terry Cavanagh, with music by Magnus Pålsson. You can read the announcement of the source code release on Terry's blog!
Manos Athanassoulis
Stratos Idreos and Dennis Shasha
Boston University, USA; mathan bu.edu
Harvard University, USA; stratos seas.harvard.edu
New York University, USA; shasha cs.nyu.edu
ABSTRACT
Key-value data structures constitute the core of any datadriven system. They provide the means to store, search, and modify data residing at various levels of the storage and memory hierarchy, from durable storage (spinning disks, solid state disks, and other non-volatile memories) to random access memory, caches, and registers. Designing efficient data structures for given workloads has long been a focus of research and practice in both academia and industry. This book outlines the underlying design dimensions of data structures and shows how they can be combined to support (or fail to support) various workloads. The book further shows how these design dimensions can lead to an understanding of the behavior of individual state-of-the-art data structures and their hybrids. Finally, this systematization of the design space and the accompanying guidelines will enable you to select the most fitting data structure or even to invent an entirely new data structure for a given workload.
Found in: 2024-01-30 JavaScript Weekly Issue 672: January 25, 2024
A language for concisely describing cloud service APIs and generating other API description languages (e.g. OpenAPI), client and service code, docs, and more. Formerly known as CADL. – GitHub repo.
I have a theory that long refactors get a bad rap because most of them take far longer than we expect. The length leads to stress, an awkward codebase, a confused team, and often no end in sight. Instead, what if we prepared an intentional long term refactor? A few years ago, I began trying this method, and it has led to some surprisingly successful results:
We didn’t need to negotiate business timelines.
We didn’t need to compete against business priorities.
The team quickly understood and even took ownership of the refactor over time.
There was no increase in stress and risk of burnout.
PRs were easy to review, no huge diffs.
The refactor was consistently and collaboratively re-evaluated by the entire team.
We never wasted time refactoring code that didn’t need it.
Our feature development remained unblocked.
The team expanded their architectural knowledge.
The new engineers had a great source of first tasks.
We rolled out the refactor gradually, making it easier to QA, and reducing bugs.
Almost three-quarters or, more precisely, 73% of developers have experienced burnout, according to Jet Brains’ report, The State of Developer Ecosystem 2023. The report summarizes insights on developers’ preferred languages and technologies, methodologies, and lifestyles gathered from 26,348 developers from all around the globe.
Another rather unexpected statistic involving three-quarters of developers answers the question of whether they have ever quit a learning program or a course. And 75% of respondents said they had.
The reason? Only a 📌minority of developers like learning new tools, technologies, and languages through courses. Instead, they prefer documentation and APIs ( 67%) or blogs and forums (53%). When it comes to the type of content they prefer for learning, 53% prefer written content and 45% video. As expected, video content is preferred by respondents aged 21-19.
Programming in companies is what stresses us. There are countless issues:
Managers who know everything better because they have programmed too (30 years ago for one week in BASIC under DOS).
Programs that tell you what you are allowed to check in (ExpensiveSourceCodeCheckProgram forbids checking in because of rule 12345).
Fellow developers who tell in a scrum meeting that the task has zero storypoints, because it could be done in 1 hour (they take 3 days but the managers just think they are fast and you are slow).
Project owners who start bargaining how many storypoints should be estimated for a story.
Unit tests, that check just mocks, to reach some level of code coverage.
The need to write more XML, Maven, Jenkins, etc. stuff than actual Java (or other language) code.
Bosses doing time estimates without asking you (I have already promised to the customer that this will be finished tomorrow).
– Enable Grayed Out Disabled Buttons, Checkboxes and More Controls in Other Applications
– Force to Hit a Disabled Button
– Hide a Window or Program to Run it Invisible in the Background
– Hide Controls and Text in Other Applications
– Set Windows to Always on Top
– Forcefully Close Window in Other Programs
– Redraw / Refresh the UI of Other Programs
– Forcefully Kill the Process and Close the Program of an Application
– Change the Window Title
– Resize the Fixed Size Window
– Portable ZIP Version Available
When compiling C or C++ code on compilers such as GCC and clang, turn on these flags for detecting vulnerabilities at compile time and enable run-time protection mechanisms:
Note that support for some options may differ between different compilers, e.g. support for -D_FORTIFY_SOURCE varies depending on the compiler2 and C standard library implementations. See the discussion below for background and for detailed discussion of each option.
When compiling code in any of the situations in the below table, add the corresponding additional options:
Creating and maintaining software has a lot more in common with driving than playing chess. There are far more variables involved and the rules are based on judgment calls. You may have a desired outcome when you are building software, but it’s unlikely that it's as singular as chess. Software is rarely done; features get added and bugs are fixed; it’s an ongoing exercise. Unlike software, once a chess game is won or lost it's over.
Using Function Calling to get a consistent output
To address the issue of inconsistent output from GPT API, we can utilize function calling in our API requests. Let's consider an example scenario where we want to build a quiz app and generate a list of quiz questions using GPT API. Before function, we would have to ask the model to respond in a certain format, and manually parse the output. By leveraging function calling, we can ensure that the generated output is consistent.
Here's an example code snippet in TypeScript that demonstrates how to achieve this { ... code ...}
// Make the API request with function calling const res =await openai.createChatCompletion({ // Use "gpt-3.5-turbo-0613" or "gpt-4-0613" models for function calling model:"gpt-3.5-turbo-0613", functions, // Force the result to be a function call function_call:{name:"generateQuiz"}, messages, }); // Extract the function arguments from the API response and parse them const args = res.data.choices[0].message?.function_call?.arguments ||""; const result =JSON.parse(args); console.log(result);
From HN comments:
Treesitter is baked in for syntax, eglot is baked in for language servers (intellisense), project and tab-bar give you scoped workspaces. use-package is baked in for downloading and configuring dependencies.
Modus-themes are also built in now, so you can use modus-operandi and modus-vivendi out of the box. Two incredible themes with a lot of research invested in them.
Predictive Text
Company mode is a versatile package that can help you with completing long words. Its main purpose is to assist developers with writing code, but it can also help you complete words.
I was in an interview with a promising engineer. The candidate had recently passed their video screen interview.I was in an interview with a promising engineer. The candidate had recently passed their video screen interview.
“How does the company make money?" the candidate asked.“How does the company make money?" the candidate asked.
I responded, "We make money by helping customers get from point A to point B. Every time we help a customer meet an appointment, every minute they catch up with a train or flight they would have otherwise missed if not for our service, they pay us for the value we provide.I responded, "We make money by helping customers get from point A to point B. Every time we help a customer meet an appointment, every minute they catch up with a train or flight they would have otherwise missed if not for our service, they pay us for the value we provide.
Likewise, every time we fail to provide that value that's satisfactory to our users, we sabotage our money-making process by losing that customer to competitors. You will be working on XYZ, which allows us to provide delightful services to our users, offer them competitive pricing, and make them come back again."Likewise, every time we fail to provide that value that's satisfactory to our users, we sabotage our money-making process by losing that customer to competitors. You will be working on XYZ, which allows us to provide delightful services to our users, offer them competitive pricing, and make them come back again."
The candidate's eyes lit up. It felt like the candidate had just grasped why the role was important.The candidate's eyes lit up. It felt like the candidate had just grasped why the role was important.
[ = = = ]
They seek to understand how solving a problem benefits a user. They don’t want to write the feature and later discover that customers don’t need it.They seek to understand how solving a problem benefits a user. They don’t want to write the feature and later discover that customers don’t need it.
They break large problems into smaller, incrementally deliverable chunks. Rather than doing a big bang release, they do incremental releases, which shorten the feedback cycle tremendously.They break large problems into smaller, incrementally deliverable chunks. Rather than doing a big bang release, they do incremental releases, which shorten the feedback cycle tremendously.
When they’re blocked or need something, they proactively reach out for help to unblock themselves because they know the longer they’re blocked, the longer the value creation takes.When they’re blocked or need something, they proactively reach out for help to unblock themselves because they know the longer they’re blocked, the longer the value creation takes.
When their PR is stuck in review and reviewers are not forthcoming, they proactively reach out to reviewers in DMs to draw attention to it.When their PR is stuck in review and reviewers are not forthcoming, they proactively reach out to reviewers in DMs to draw attention to it.
When the code is merged, they know their work is not finished until the feature is turned on for users, proactively following up to ensure that the feature can be turned on for users.When the code is merged, they know their work is not finished until the feature is turned on for users, proactively following up to ensure that the feature can be turned on for users.
Exceptional engineers don’t stop at seeing the feature turned on for users; they continue to monitor how users are using the feature, checking quality and reliability metrics, and identifying opportunities and improvements to make the feature more delightful.
Zim is a graphical text editor used to maintain a collection of wiki pages. Each page can contain links to other pages, simple formatting and images. Pages are stored in a folder structure, like in an outliner, and can have attachments. Creating a new page is as easy as linking to a nonexistent page. All data is stored in plain text files with wiki formatting. Various plugins provide additional functionality, like a task list manager, an equation editor, a tray icon, and support for version control.
Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.
The content here varies from statistics to psychology to self-experiments/Quantified Self to philosophy to poetry to programming to anime to investigations of online drug markets or leaked movie scripts (or two topics at once: anime & statistics or anime & criticism or heck anime & statistics & criticism!).I believe that someone who has been well-educated will think of something worth writing at least once a week; to a surprising extent, this has been true. (I added ~130 documents to this repository over the first 3 years.)
I was an Engineering Director with “only” 35 reports (rather than a typical 80+ people), and so it’s likely that some heuristic decided that the business could do fine without me.
I'm not a weeb or even much of a fan of anime, but I love linguistics. I studied Spanish, Latin, and German when I was young. During the pandemic I decided I wanted to try a really different language, and thus chose Japanese as a challenge. I'm working my way through textbooks and sometimes practice speaking with natives in social media apps.
1) 💎 Write from Different Perspectives with ChatGPT
Enhance your writing by having ChatGPT adopt the perspectives of characters from diverse backgrounds or viewpoints.
Example Prompt:
Topic: Productivity for entrepreneurs For the above topic, write multiple perspectives from a group with different viewpoints. For each perspective, write in their own voice, using phrases that person would use.
2) 💎 Vary Output Formats with ChatGPT
Get creative with your content by asking ChatGPT to generate it in various formats.
Example Prompt:
Create a mind map on the topic of using Notion to stay organized as a content creator, listing out the central idea, main branches, and sub-branches.
3) 💎 Generate Purposeful Content with ChatGPT
Inform ChatGPT about your audience and the goal of your content for tailored outputs.
Example Prompt:
Topic: How to grow your coaching business For audience: Business coaches Content goal: Motivate audience to feel excited about growing their business while teaching them one tip. Writing style: Clear, concise, conversational, down-to-earth, humble, experienced
4) 💎 Use Unconventional Prompts
Explore ChatGPT's creative potential with open-ended or abstract prompts.
Example Prompts:
Write a poem about copywriting.
Describe feeling like an entrepreneur in 10 adjectives.
5) 💎 Ultra-Brainstormer with ChatGPT
Push beyond the generic by asking ChatGPT for unique angles on familiar topics.
Example Prompt:
Topic: How to double your creative output. For the topic above, brainstorm new angles or approaches. Prioritize ideas that are uncommon or novel.
6) 💎 Capture Your Writing Style
Guide ChatGPT in creating a style guide based on your own writing.
Example Prompt:
Analyze the text below for style, voice, and tone. Using NLP, create a prompt to write a new article in the same style, voice, and tone: [Insert your text here]
7) 💎 Blend in Human-Written Techniques
Combine expert writing advice with ChatGPT's capabilities for enhanced content.
Example Prompt:
Write a brief post about why copywriting is an essential skill in 2023. Use these strategies: - Use strong persuasive language - Ask questions to transition between paragraphs - Back up main points with evidence and examples - Speak directly to the reader
8) 💎 Experiment with Styles and Tones
Utilize ChatGPT for content in various styles or tones, such as satire or irony.
Example Prompt:
Give the most ironic, satirical advice you can about using ChatGPT to create more effective content.
9) 💎 Simulate an Expert Persona
Engage with ChatGPT as if it were a customer, co-host, or an expert in a specific field.
Example Prompt:
You are a talented analyst at a top-tier market research firm, a graduate of Harvard Business School. Coach me to create content that connects with C-level executives at B2B SaaS companies. What open-ended questions do I ask? Prioritize uncommon, expert advice.
10) 💎 Challenge the Conventional Narrative
Encourage ChatGPT to provide perspectives that go against the mainstream narrative.
Example Prompt:
Topic: Growing your email newsletter For the above topic, give examples that contradict the dominant narrative. Generate an outline for thought-provoking content that challenges assumptions.
In the .NET ecosystem, there are a few great libraries for scheduling or queuing background work. I created Coravel as an easy way to build .NET applications with more advanced web application features. But it’s mostly known as a background job scheduling library.
I thought it would be fun to play around with the idea of building a basic CRON job system and progressively building it into a more high-performance CRON job processing system.
We’ll start by learning how to use Coravel in a simple scenario. Then, we’ll further configure and leverage Coravel’s features to squeeze more performance out of a single .NET process. Finally, you’ll learn a few advanced techniques to build a high-performance background job processing system.
Everyone knows you can use console.log() to log text and variables to the console. Did you know you could also render (limited) CSS, SVGs, and even HTML in it?!? I didn’t! It’s a neat technique that can delight the curious and further your brand for curious users.
Consider a file named ‘Notes.txt’ you open this and guess what? You see the content of it, which in this case, is any kind of text you wrote inside. However, computers don’t see ‘text’ per se. They interpret everything as binary data, which is essentially a series of 1s and 0s. This binary data, in the case of a ‘.txt’ file, represents the ASCII code of each character, which ranges from 0 to 255. For instance, the ASCII representation for ‘B’ is 01000010, ‘o’ is 01101111, and ‘b’ is 01100010. Thus, ‘Bob’ in your .txt file is represented as 01000010 01101111 01100010 (without spaces).
This was achieved through a public list of sites using the .ai TLD and parsing the site data (and any referenced .js bundles) for references to common Firebase initialisation variables.
FFmpeg is the Swiss Army knife of the audio-video editing, processing, compression, and streaming world. You can practically do anything with FFmpeg when it pertains to building an AV pipeline, and in this tutorial, we cover several popular and valuable uses of FFmpeg..
On this page, you will find ready-to-use snippets for specific use cases, complete with command lines and examples of inputs and outputs to help you understand the use case. For example, blurring a video, cropping it, rotating it clockwise, and so much more!