Guide to Writing Effective Pseudocode for Legacy Code Analysis
Purpose
This guide will help you write clear, consistent pseudocode to document and understand legacy code. Good pseudocode serves as a bridge between human thinking and computer code, making complex logic easier to comprehend and discuss with team members.
Core Principles
1. Use Natural Language with Structure
Write in plain English (or your team's primary language) while maintaining a clear programming-like structure. This makes the code logic accessible while preserving important algorithmic concepts.
Example:
# INSTEAD OF:
customers.each do |customer|
send_notification(customer) if customer.balance.negative?
end
# WRITE:
FOR EACH customer in customer list
IF customer's balance is negative
send notification to customer
2. Maintain Consistent Indentation
Use indentation to show hierarchy and control flow, just like in regular code. Each nested block should be indented by 4 spaces or 1 tab.
Example:
FUNCTION process monthly transactions
GET all transactions for current month
FOR EACH transaction
IF transaction is pending
verify transaction details
IF verification successful
mark as processed
ELSE
add to review queue
ELSE IF transaction is completed
update account balance
3. Legacy Code Specific Practices
Document Assumptions
When analyzing legacy code, explicitly state assumptions about the code's behavior:
# Ruby Implementation:
def update_user_permissions(user, new_role)
raise unless current_user.role_rank > user.role_rank
user.update_role(new_role)
LinkedSystem.propagate_changes(user)
end
# Pseudocode with Assumptions:
FUNCTION update user permissions
// ASSUMPTION: User roles are hierarchical (Admin > Manager > User)
// ASSUMPTION: Changes propagate to linked systems
GET user's current role
IF role change is requested
verify requester has higher role
update role in main system
trigger linked system updates
Mark Unclear Areas
Highlight portions of logic that need clarification:
# Ruby Implementation:
def process_payment(payment)
if payment.legacy_type?
# Multiple processors found in codebase:
# - LegacyProcessor.process (app/services/legacy/processor.rb)
# - OldPaymentProcessor.handle (lib/payment/processor.rb)
processor.route_payment(payment)
end
end
# Pseudocode:
FUNCTION process payment
validate payment details
IF payment method is legacy type
// UNCLEAR: Multiple legacy payment processors found
// TODO: Verify which processor handles this type
route to payment processor
update transaction record
4. Standard Keywords and Their Usage
Use these consistent keywords to maintain clarity:
Control Flow
- BEGIN / END: Mark the start and end of a block
- IF / ELSE IF / ELSE: Conditional logic
- FOR EACH: Iteration over a collection
- WHILE: Loops with conditions
- FUNCTION: Define a procedure
- RETURN: Specify output
Data Operations
- GET: Retrieve data
- SET: Assign value
- UPDATE: Modify existing data
- DELETE: Remove data
- COMPUTE: Calculate a value
Common Patterns Example
Error Handling
# Ruby Implementation:
def process_sensitive_data(data)
begin
raise InvalidDataError unless data.valid?
processed_data = ProcessingService.process(data)
processed_data.save!
rescue StandardError => e
Rails.logger.error(e)
AdminNotifier.alert(e)
false
end
end
# Pseudocode:
FUNCTION process sensitive data
BEGIN TRY
validate input data
IF data is corrupt
THROW data validation error
process data
save results
BEGIN CATCH
log error details
notify system admin
RETURN error status
END CATCH
Asynchronous Operations
# Ruby Implementation:
def fetch_user_profile
Thread.new do
spinner.start
user_data = Concurrent::Promise.new do
[
UserBasicInfo.fetch,
UserPreferences.fetch,
UserHistory.fetch
]
end.then do |basic, prefs, history|
UserProfileBuilder.new(basic, prefs, history).build
end.value!
spinner.stop
user_data
end
end
# Pseudocode:
FUNCTION fetch user profile
BEGIN ASYNC
start loading indicator
PARALLEL
GET user basic info
GET user preferences
GET user history
WAIT FOR ALL
combine user data
update display
stop loading indicator
END ASYNC
Best Practices for Legacy Code Analysis
-
Start High-Level
Begin with a broad overview before diving into details:SYSTEM payment processing flow receive payment request validate payment details route to appropriate processor handle processor response update transaction records -
Document Integration Points
# Ruby Implementation: def sync_customer_data # Fetches from Salesforce API updated_records = CRM::Client.fetch_updates # Updates Stripe customers updated_records.each do |record| BillingSystem.update_customer(record) end # Uses SendGrid EmailService.send_sync_notification end # Pseudocode: FUNCTION sync customer data // EXTERNAL: CRM System GET updated customer records from CRM // EXTERNAL: Billing System FOR EACH customer update billing system // EXTERNAL: Email Service send sync completion notification -
Mark Business Rules
# Ruby Implementation: def apply_discount(customer, amount) discount = calculate_base_discount(amount) discount += premium_discount if customer.premium? [discount, MAX_DISCOUNT].min end # Pseudocode: FUNCTION apply discount // BUSINESS RULE: Premium customers get additional 5% // BUSINESS RULE: Maximum discount is 30% calculate base discount IF customer is premium add premium discount IF total discount > maximum allowed set discount to maximum
Practice Exercise
Try converting this legacy Ruby code into pseudocode:
def process_order(order)
return false if order.items.blank?
total = 0
order.items.each do |item|
next unless item.in_stock?
total += item.price * item.quantity
item.update_stock_level(-item.quantity)
end
if total.positive?
order.update(
total: total,
status: 'processed'
)
CustomerNotifier.notify_processed(order.customer_id)
true
else
false
end
end
Solution:
FUNCTION process order
IF order has no items
RETURN failure
SET total to 0
FOR EACH item in order items
IF item is in stock
add item total to order total
decrease item stock level
IF order total is greater than 0
set order total
mark order as processed
notify customer
RETURN success
RETURN failure
Book Project Setup
Here's the pseudocode for project_setup.pseudocode:
SYSTEM BookProjectSetup
PROCEDURE main
BEGIN PROJECT SETUP
INITIALIZE project with default options:
meta_file: './scripts/meta.yaml'
content_dir: 'content'
logger: STDOUT
VALIDATE metadata from meta.yaml:
REQUIRED_FIELDS = creator, copyright_year, cover_image,
description, file_name, identifier, lang, pages,
published_date, publisher, publisher_city, rights,
subject, title, type, tags
CHECK system dependencies:
asdf - runtime version manager
bundle - Ruby dependency manager
npm - Node.js package manager
IF any tool missing
ABORT with instructions to install missing tools
SETUP development environment:
INSTALL runtime versions using asdf
INSTALL Ruby gems using bundle
INSTALL Node.js packages using npm
CREATE cover images:
SOURCE: content/images/cover.svg
IF source not found
ABORT "Cover image not found"
FOR EACH format IN ["jpg", "png"]
CONVERT SVG to #{format} using ImageMagick
UPDATE project files:
MODIFY .latexmkrc:
REPLACE FILE_NAME with metadata file_name
UPDATE main.tex:
REPLACE FILE_NAME, TITLE, AUTHOR, TAGS, SUBJECT placeholders
UPDATE copyright.tex:
REPLACE COPYRIGHTYEAR placeholder
UPDATE title-page.tex:
REPLACE FILE_NAME, TITLE, AUTHOR, PUBLISHER,
PUBLISHERCITY, TAGS, SUBTITLE placeholders
UPDATE half-title.tex:
REPLACE TITLE placeholder
FOR EACH .tex file IN content directory
REPLACE FILE_NAME placeholder
SHOW success message with completion status
END PROJECT SETUP
Here's the pseudocode for tooling_setup.pseudocode:
SYSTEM Setup Tools Installation
PROCEDURE main
BEGIN PROJECT SETUP
INITIALIZE setup with default options:
REQUIRED_BREW_PACKAGES = [
"pandoc",
"pandoc-crossref",
"librsvg",
"imagemagick",
"ghostscript",
"mactex",
"skim",
"calibre",
"lua",
"diff-pdf",
"tidy-html5",
"epubcheck",
"tesseract"
]
REQUIRED_CASK_PACKAGES = [
"kindle-previewer"
]
REQUIRED_TLMGR_PACKAGES = [
"collection-fontsrecommended",
"collection-latexrecommended",
"collection-latexextra",
"collection-pictures",
"collection-bibtexextra"
]
VALIDATE system requirements
IF not macOS
ABORT "This script is designed for macOS only."
UPDATE Homebrew
RUN brew update
RUN brew upgrade
INSTALL Brew Packages
FOR EACH package in REQUIRED_BREW_PACKAGES
CHECK if package is installed
IF not installed
INSTALL package
END FOR
INSTALL Cask Packages
FOR EACH cask in REQUIRED_CASK_PACKAGES
CHECK if cask is installed
IF not installed
INSTALL cask
END FOR
UPDATE LaTeX Packages
SET PATH to include TeX Live
WAIT 30 seconds for MacTeX installation
CHECK for tlmgr updates
IF updates available
UPDATE tlmgr
UPDATE all packages
INSTALL REQUIRED_TLMGR_PACKAGES:
FOR EACH package
IF not installed
INSTALL package
END FOR
RUN texhash to update file database
UPDATE Kindle Previewer
FIND application path in standard locations
IF found
FIND CLI tool path
CHECK current version
RUN update check
IF update available
INITIATE update
WAIT 30 seconds for completion
VERIFY new version
ELSE
SHOW up-to-date message
ELSE
ABORT with instructions to install Kindle Previewer
SHOW success message with completion status
END PROJECT SETUP