Category: Programming

  • SDLC planning phase: From feasibility to maintenance

    SDLC planning phase: From feasibility to maintenance

    The SDLC planning phase turns a software idea into an approved, bounded project. It defines the problem, tests whether the solution is feasible, sets scope, identifies stakeholders and risks, and establishes the resources and schedule needed for delivery.

    Planning is separate from detailed requirements analysis. It decides whether and how to pursue the work; requirements work later defines what the system must do in verifiable detail.

    What happens in the SDLC planning phase?

    Planning creates a shared delivery baseline before analysts and developers begin detailed discovery. The team typically:

    • Defines the problem: states the user need, expected outcome, and reason the project matters.
    • Checks feasibility: assesses technical capability, budget, timing, operational fit, and major constraints.
    • Sets scope: records included features, exclusions, assumptions, dependencies, and success measures.
    • Maps stakeholders: identifies the sponsor, users, product owner, delivery team, operations staff, and approvers.
    • Plans resources and schedule: assigns roles, tools, environments, budget, milestones, and an initial delivery sequence.
    • Identifies risks: records threats such as integration failure, unclear ownership, data loss, or unrealistic deadlines, along with mitigations.

    Where does the planning phase of SDLC fit?

    The planning phase of SDLC comes before requirements, design, implementation, testing, deployment, and maintenance. It can be revisited when new evidence changes scope, cost, or risk, but it should provide an approved starting point for the next phase.

    Planning answers, “Should this project proceed, and under what boundaries?” Requirements analysis answers, “What must the product do?” Keeping those decisions separate prevents a rough project idea from being mistaken for a complete specification.

    Which planning outputs and exit criteria let requirements work begin?

    Useful planning outputs make the project understandable and governable. They commonly include:

    • A problem statement or project charter
    • A feasibility assessment and initial business case
    • A scope statement with exclusions and success measures
    • A stakeholder register and responsibility outline
    • A resource plan and milestone schedule
    • A risk register with owners and response actions

    Requirements work can begin when the sponsor or product owner approves the problem, scope, feasibility case, initial resources, and delivery approach. The team should also know who can make decisions and how risks will be escalated. Detailed user stories, functional requirements, acceptance criteria, and the requirements specification are outputs of the next phase, not substitutes for the planning package.

    What are the SDLC phases with examples for one small app?

    Consider a shared grocery-list app for households. Its limited purpose is to let invited users add, edit, and mark items as purchased.

    1. Planning: The team confirms that households need a shared list and that a mobile-friendly web app is technically and financially feasible. Version one excludes recipes, payments, and delivery integration. Two developers, one designer, and a product owner receive a four-week schedule. A key risk is conflicting edits, with synchronization rules planned as a mitigation.
    2. Requirements: Analysts interview household users and document stories such as “add an item,” “assign a quantity,” and “mark an item purchased.” They define permissions, error messages, and acceptance criteria for each story.
    3. Design: The team creates list-screen wireframes, a responsive layout, an invitation flow, and a data model for users, lists, and items. It also chooses an approach for handling simultaneous edits.
    4. Implementation: Developers build the interface, authentication, list APIs, database, and synchronization behavior within the approved scope.
    5. Testing: Testers check item updates, permissions, validation, synchronization, browser compatibility, and usability against the requirements and acceptance criteria.
    6. Deployment: The team releases the app to a small household pilot, configures production monitoring, and provides support instructions.
    7. Maintenance: The team fixes defects, monitors failures, improves synchronization, and evaluates requests such as recurring items for a later release.
  • Python Drawing with Turtle: Commands for Beginners

    Python Drawing with Turtle: Commands for Beginners

    Python drawing is straightforward with the standard turtle module. This example uses Python turtle graphics to draw a blue square, move without leaving a line, and add a centered label.

    The complete example also shows how to draw in Python with movement, turns, pen controls, a loop, coordinates, color, line width, and turtle.write.

    Python Drawing: Create a Turtle Screen and Pen

    Import the built-in module first. Then create a screen and a turtle object that acts as your drawing pen:

    import turtle
    screen = turtle.Screen()
    pen = turtle.Turtle()

    screen is the drawing window, while pen stores the turtle’s position, direction, color, and pen state. A new turtle starts at coordinate (0, 0), usually facing right. The screen uses ordinary x and y coordinates: positive x moves right, and positive y moves up.

    Use Python turtle commands to Move, Turn, and Repeat a Shape

    The main Python turtle commands control distance and direction. pen.forward(120) moves 120 pixels in the current direction and draws a line. pen.backward(40) moves backward while drawing. pen.left(90) and pen.right(90) turn the turtle by 90 degrees without moving it.

    A square needs four equal sides and four 90-degree turns. A for loop repeats those two actions:

    for _ in range(4):
        pen.forward(120)
        pen.left(90)

    The underscore means that the loop count itself is not needed. Each repetition draws one side and turns the turtle for the next side. When the loop ends, the turtle returns to its starting coordinate and direction.

    How to draw in Python: Control Position, Color, Width, and Pen State

    Use pen.penup() to move without drawing and pen.pendown() to resume drawing. This prevents an unwanted line when moving from the completed square to its label.

    pen.goto(60, 60) moves directly to a specific coordinate. With the pen raised, it creates no line. Use pen.color(“royalblue”) to set the drawing color and pen.width(3) to make the outline three pixels wide. Call these methods before the action they should affect.

    For example, set the square’s color and width before the loop, then raise the pen before using goto(). If a diagonal line appears between the square and the label, penup() was omitted or called too late.

    Add a Label with turtle.write and Keep the Window Open

    Run these lines in order. The two indented lines belong inside the loop:

    1. import turtle
    2. screen = turtle.Screen()
    3. pen = turtle.Turtle()
    4. pen.shape(“turtle”)
    5. pen.color(“royalblue”)
    6. pen.width(3)
    7. for _ in range(4):
      • pen.forward(120)
      • pen.left(90)
    8. pen.penup()
    9. pen.goto(60, 60)
    10. pen.pendown()
    11. pen.color(“darkgreen”)
    12. turtle.write(“Square”, align=”center”, font=(“Arial”, 16, “bold”))
    13. screen.mainloop()

    turtle.write() places text at the turtle’s current position. Here, align=”center” centers “Square” at coordinate (60, 60), while the font setting specifies the typeface, size, and bold style. The module-level function writes at the active turtle’s position; pen.write() is the equivalent object-based form.

    screen.mainloop() keeps the turtle window open after the drawing finishes. The finished result is a 120-by-120 blue square with a green label centered inside it.

  • Size of an Array in C++: Count Elements Safely

    Size of an Array in C++: Count Elements Safely

    Use std::size(array) for a built-in array in C++17 and later. You can also divide sizeof(array) by sizeof(array[0]) while the compiler still knows the complete array type. Both methods return the number of elements, not the array’s size in bytes.

    For example, a five-element array has a C++ array length of 5. The key limitation is array-to-pointer decay: after an array becomes a pointer, neither method can recover the original element count. This guide shows how to get an array’s size in C++ safely and when to use .size() instead.

    Size of an Array in C++ with std::size: a five-element example

    std::size is the clearest standard-library option for counting elements in a built-in array. Include <iterator>, then pass the array directly to the function. It returns the array’s element count as a size type, normally std::size_t.

    Example: #include <cstddef>; #include <iterator>; int scores[] = { 12, 18, 24, 31, 40 };; std::size_t count = std::size(scores);. The value of count is 5.

    The compiler determines the bound from the array type, which is int[5] in this example. You do not need to repeat the bound or maintain a separate constant. Unlike a member function, std::size works with a built-in array even though built-in arrays do not have a .size() member.

    This overload preserves the array type by receiving it through a reference. That detail matters because passing the array in the wrong context can change it into a pointer before the count is calculated.

    Use the C++ sizeof operator for an array: division returns 5

    The traditional C++ sizeof for an array formula divides the total number of bytes occupied by the array by the number of bytes occupied by one element:

    Formula: sizeof(array) / sizeof(array[0])

    Applied to the same five-element array, the code is std::size_t count = sizeof(scores) / sizeof(scores[0]);. The result is 5. The numerator represents all five elements, while the denominator represents one int. The calculation therefore produces an element count regardless of the platform’s actual int size.

    sizeof(scores) is not itself the array length. It reports the array’s total storage in bytes. Similarly, sizeof(scores[0]) reports the storage for one element. The division is valid only when scores still has its array type, such as in the same scope where it was declared or in a function that receives an array by reference.

    std::size is usually preferable in modern C++ because it states the intent directly and avoids repeating the element expression. The sizeof formula remains useful in older language standards and in low-level code where the array type is known.

    Why is C++ array size lost after array-to-pointer decay?

    When a built-in array is passed to an ordinary function parameter, it normally changes, or decays, into a pointer to its first element. Bracket notation in a parameter does not prevent this adjustment. These declarations therefore describe the same effective parameter:

    void report(const int values[]); and void report(const int* values);

    Inside report, values is a pointer, not an array. Consequently, sizeof(values) returns the pointer’s storage size, while sizeof(values[0]) returns the size of one integer. Dividing those values produces an unrelated result rather than the original number of elements. The result may vary by platform and pointer type, so never apply the array division formula to a pointer.

    std::size(values) also cannot count the original array in this function. Its array overload requires an actual array, and a pointer does not contain a record of how many elements were allocated or where the array ends. A pointer might refer to one element, a dynamically allocated block, or only a subrange of a larger array.

    Pass the count separately when a function intentionally accepts a pointer:

    void report(const int* values, std::size_t count);

    Alternatively, accept the built-in array by reference so its bound remains available:

    template <typename T, std::size_t N> constexpr std::size_t countOf(const T (&values)[N]) { return N; }

    Calling countOf(scores) returns 5. The reference parameter prevents array-to-pointer decay, and N captures the bound at compile time.

    How do std::array and std::vector report C++ array length? .size() returns the element count

    std::array is a fixed-size container that keeps its element count as part of its type. Its .size() member returns the number of stored elements:

    std::array<int, 5> fixedScores{ 12, 18, 24, 31, 40 };; fixedScores.size() returns 5.

    The value cannot change during the lifetime of that std::array. An std::array<int, 0> is valid, and its .size() returns 0. Use this container when you want array-like storage with standard container interfaces, iterators, and a reliable size operation.

    std::vector stores a variable number of elements. Its .size() member returns the number of elements currently stored, not the amount of memory reserved:

    std::vector<int> dynamicScores{ 12, 18, 24, 31, 40 };; dynamicScores.size() returns 5. After dynamicScores.push_back(47), it returns 6. Use .capacity() only when you need the allocated storage capacity; it is not the C++ array length.

  • Variable in Programming: Names, Values, Types, and Scope

    Variable in Programming: Names, Values, Types, and Scope

    A variable in programming is a named reference to data that a program can read and, when permitted, update. The name is not the value: score is the name, while 10 is its current value. Code uses the name in expressions instead of repeating the data, so the same value can be retrieved wherever that variable is visible.

    Variables give changing information a stable handle. A program might store a user’s score, a filename, or whether a task is complete. The handle remains meaningful even when its current value changes.

    Variable in programming: What does it represent?

    A variable can be understood through four related properties:

    • Name: The identifier used by code, such as score.
    • Current value: The data associated with that name at a particular moment, such as 10.
    • Type: The category of data, such as a number, text value, or true-or-false value.
    • Scope: The region of code where the name can be used.

    When code reads score, it obtains the value currently associated with that name. If the program later changes score to 11, the name stays the same but its current value differs. A variable is a language-level abstraction; implementations do not all represent it as one fixed raw memory location.

    What are variables in programming, and how are they assigned?

    The basic variable lifecycle has four steps:

    1. Declaration: Introduce a name, as in declare score. Some languages require a declared type here.
    2. Initialization: Give the variable its first value, such as score = 10. This first assignment is initialization.
    3. Reading: Use the name to obtain its value, for example, total = score + bonus.
    4. Reassignment: Replace the current value with another, such as score = 11.

    Real languages use different syntax. A declaration and initialization may appear in one statement, while another language may infer the type from the first value. The underlying actions remain recognizable: create or introduce a name, associate data with it, read that data, and optionally associate a new value with the name.

    Assignment normally evaluates the expression on the right before updating the name. In score = score + 1, the program reads the old score, adds one, and stores the result as the new score.

    How do types affect changing values?

    A type describes what kind of value a variable represents and which operations are appropriate. A number can support arithmetic, text can support joining or searching, and a Boolean value represents either true or false. For example, age = 30 and name = “Mina” associate different kinds of data with their names.

    Some languages require a variable to keep one declared type. In that setting, assigning text to age may produce an error. Other languages allow a name to refer to values of different types over time, so item = 30 can later become item = “thirty”. Type rules vary, but they help a language detect invalid operations and determine how values should be handled.

    How does scope affect variables in programming?

    Scope is the part of a program where a variable name is available. A variable declared inside a function or block is usually local: code outside that region cannot read it directly. For example, a local total created inside calculate belongs to that operation and disappears from direct use when the scope ends.

    A variable declared in a wider module or program scope can be accessible to several functions, depending on the language. Wider scope can make shared state convenient, while local scope limits accidental changes and makes a function easier to understand. Two separate scopes may contain variables with the same name; each name then refers to the value belonging to its own scope. A local variable can also temporarily hide a wider variable with that name, a behavior commonly called shadowing.

  • Rails migration: Generate, add references, and run

    Rails migration: Generate, add references, and run

    A Rails migration is a versioned Ruby file that changes your database schema. The usual workflow is to generate a named migration, edit its change method, run it with bin/rails db:migrate, verify the result, and roll it back when necessary.

    Use the generator for predictable column and reference definitions, but inspect the generated file before applying it. Rails places migration files in db/migrate and prefixes each filename with a timestamp.

    Generate a Rails migration: named command, Ruby file, and change method

    Generate a named migration with a descriptive CamelCase name:

    bin/rails generate migration AddStatusToOrders

    Rails creates a file similar to db/migrate/20240101000000_add_status_to_orders.rb. The timestamp will differ in your project. Its contents typically look like this:

    class AddStatusToOrders < ActiveRecord::Migration[7.1]
      def change
        add_column :orders, :status, :string
      end
    end

    The migration version in brackets follows your application’s Rails version and may be different. The change method describes the forward operation. Rails can infer the reverse operation for standard commands such as add_column, so a rollback can remove the column automatically.

    Use rails generate migration to add columns

    You can pass column definitions directly to rails generate migration:

    bin/rails generate migration AddDetailsToUsers name:string age:integer active:boolean

    This generally generates an add_column operation for each attribute in the migration file. Review and edit the file if the column needs a default, a limit, or a null constraint:

    add_column :users, :name, :string, null: false
    add_column :users, :age, :integer
    add_column :users, :active, :boolean, default: true, null: false

    Use a migration name that states both the action and the table, such as AddPublishedAtToArticles. The name helps you identify the migration in status output and deployment history; it does not replace checking the generated Ruby.

    Rails migration references: index and foreign_key options

    Generate a reference column with the references attribute:

    bin/rails generate migration AddUserToPosts user:references

    This creates a user_id column on posts through an add_reference operation. To state the intended database behavior explicitly, use:

    add_reference :posts, :user, index: true, foreign_key: true

    The index: true option creates an index on posts.user_id, which improves lookups and supports common association queries. The foreign_key: true option adds a database foreign-key constraint from posts.user_id to users.id, following Rails’ naming convention.

    Use a custom target when the reference points to a differently named table:

    add_reference :posts, :author, foreign_key: { to_table: :users }

    Keep the index unless you have a specific reason not to use one. If the column must accept no missing value, add null: false only after existing rows can satisfy that constraint.

    Rails migrate: run db:migrate, check status, roll back, or revise

    Apply all pending migrations with:

    bin/rails db:migrate

    Rails records each applied migration in the database’s internal migration table. Check which files are applied or pending with:

    bin/rails db:migrate:status

    The status output marks migrations as up or down and shows each migration’s version and name. Confirm the change in db/schema.rb or db/structure.sql, depending on your project, and verify the new column or index in the database.

    Roll back the most recent migration with:

    bin/rails db:rollback STEP=1

    Increase STEP to reverse several recent migrations. For a specific migration, use its version:

    bin/rails db:migrate:down VERSION=20240101000000

    If a migration is still down, edit its original file, run it, and check the status again. If it is already up in a shared environment, do not edit that historical file. Generate a new migration that changes or reverses the applied schema instead. On a private local branch, you can roll the migration back first, edit it, and rerun it when no other environment depends on that migration’s existing behavior.

  • How Many Spaces Is a Tab? Usually 2, 4, or 8

    How Many Spaces Is a Tab? Usually 2, 4, or 8

    How many spaces is a tab? Usually, a tab appears as 2, 4, or 8 columns, depending on the editor or viewer. Technically, however, a tab is one character—not a fixed number of space characters.

    The displayed width and the stored character are separate. A tab setting controls where the next tab stop appears, while inserting spaces places individual space characters in the file.

    How many spaces is a tab?

    A tab character is commonly represented as U+0009. In UTF-8, it occupies one byte, 0x09. That character does not contain instructions such as “insert four spaces.” The program displaying it decides how far to move the text.

    If the tab width is set to four columns, tab stops occur every four columns. A tab at the beginning of a line may therefore appear four columns wide. A tab typed after text that already occupies two columns may appear only two columns wide, because it moves the cursor to the next tab stop.

    Spaces behave differently. Each ordinary space is a separate character, usually U+0020, and occupies one column in a monospace font. Four inserted spaces remain four characters regardless of the viewer’s tab setting.

    In a monospace comparison, visible markers make the difference clear: one tab [⇥]X versus four literal spaces [␠␠␠␠]X. The ⇥ marker represents one tab character, while each ␠ marker represents one space. They may create the same indentation at a four-column setting, but the file contains different characters.

    How many spaces are in a tab at 2, 4, or 8 columns?

    There are no spaces “inside” a tab. The numbers 2, 4, and 8 describe common display settings:

    • 2 columns: Often used when compact indentation is important, such as in many web and configuration files.
    • 4 columns: A common general-purpose choice that provides clearer nesting without excessive horizontal movement.
    • 8 columns: A traditional default in many terminals and tools, making tabs highly visible but potentially creating wide indentation.

    A setting of four does not guarantee that every tab occupies four visible columns. It means the display uses tab stops four columns apart. The tab’s apparent width depends on its position on the line and the current display settings.

    How does tab width in spaces affect source files?

    Source files normally store the tab character itself, not its rendered width. The file may contain a sequence such as one tab, two tabs, or a mixture of tabs and spaces. An editor then renders those characters using its configured tab size.

    As a result, the same file can look different in two editors. A line indented with one tab may align at column 4 in one editor and column 8 in another. Code that relies on visual alignment, including comments or continued expressions, can become misleading when the tab setting changes.

    Project formatters and editor settings can also rewrite indentation. A formatter may convert tabs to spaces, convert leading spaces to tabs, or enforce a selected indentation size. These rules affect the characters saved to the file, not just their appearance. A file’s visual layout is therefore controlled by both its contents and the tools that interpret or format it.

    How do you keep indentation consistent across editors?

    Choose one project-wide indentation convention and configure every relevant tool to follow it. The important settings are usually named tab size, indent size, and insert spaces or insert tabs.

    • Decide whether indentation should use tabs, spaces, or a defined combination.
    • Set the editor’s tab display width to the project’s documented value, such as 2 or 4 columns.
    • Set automatic indentation to insert the chosen character type rather than relying on each editor’s default.
    • Configure the project formatter to preserve or normalize that convention when files are saved.
    • Check existing files for mixed leading tabs and spaces before applying broad conversion.

    For a spaces-only convention, a four-level indent means four literal spaces per level. For a tabs-only convention, one tab may represent one indentation level while its visual width remains configurable. Keeping those choices explicit prevents indentation from changing when the file moves between editors, terminals, and code-review tools.

  • Pseudocode: Definition, Conventions, and Example

    Pseudocode: Definition, Conventions, and Example

    Pseudocode is a language-neutral way to describe an algorithm. It uses familiar words, named variables, and indentation to show what a program should do without requiring the exact syntax of Python, JavaScript, Java, or another language.

    A clear pseudocode plan helps you check the logic, explain it to other people, and translate it into executable code later. It focuses on actions and decisions rather than punctuation or language-specific rules.

    What Is Pseudocode, and What Is It Used For?

    Pseudocode is used to plan an algorithm before implementation. An algorithm is a defined sequence of steps for completing a task, such as calculating an average, searching a list, or processing a user’s input.

    Writing pseudocode first makes the logic easier to review. You can identify missing inputs, incorrect conditions, or endless loops before dealing with the details of a programming language. It is also useful for documentation because a reader can understand the process without knowing the language used to build it.

    Effective pseudocode should:

    • State the steps in the order they happen.
    • Name the data the algorithm reads, stores, and produces.
    • Show decisions and repeated actions explicitly.
    • Use consistent indentation and control-flow terms.

    Which Conventions Make Pseudocode Readable for Sequence, Input, Output, Conditions, and Loops?

    There is no single universal pseudocode standard. Choose clear terms and apply them consistently. These conventions cover most algorithms:

    • Sequence: Put one action after another, with each step on its own line. The order of the lines represents the order of execution.
    • Input: Use INPUT to show that the algorithm receives data, such as INPUT customer_name.
    • Output: Use OUTPUT to show displayed or returned information, such as OUTPUT total.
    • Assignment: Use SET to give a variable a value, such as SET total TO 0. Some authors use an equals sign instead.
    • Condition: Use IF, THEN, ELSE, and END IF to describe a decision. Indent the actions belonging to each branch.
    • Loop: Use FOR for a known number of repetitions and WHILE when repetition continues as long as a condition remains true. Mark the loop’s boundary with END FOR or END WHILE.

    Use descriptive variable names, such as average_price rather than x. Capitalized keywords can make control flow easy to scan, but capitalization is optional. The important rule is consistency.

    How Does Pseudo Code Differ From Executable Code and Flowcharts?

    Pseudocode is not executable code. A compiler or interpreter cannot run it because terms such as INPUT and END IF do not have one fixed technical meaning. Real code must follow the grammar, data types, operators, and libraries of a chosen programming language.

    A flowchart represents an algorithm visually with symbols, arrows, and branches. Pseudocode represents the same logic as structured text. Flowcharts can make paths and decisions immediately visible, while pseudocode is usually faster to edit, search, and convert into code. Neither format replaces careful testing.

    How to write pseudocode: A Worked Example With Variables, Indentation, and Program-Code Translation

    Suppose an algorithm must read the prices of three items, calculate the total and average, and report whether the average is above 50. The following example uses sequence, input, output, a loop, a condition, and named variables:

    • START
    • SET item_count TO 3
    • SET total TO 0
    • FOR item_number FROM 1 TO item_count
      • INPUT price
      • SET total TO total + price
    • END FOR
    • SET average_price TO total / item_count
    • IF average_price > 50 THEN
      • OUTPUT “The average price is high.”
    • ELSE
      • OUTPUT “The average price is within budget.”
    • END IF
    • OUTPUT total, average_price
    • END

    To translate this into program code, map each abstract action to the target language’s syntax. For example, a Python implementation could use total = 0 for assignment, float(input()) for numeric input, for item_number in range(1, item_count + 1) for the counted loop, and if average_price > 50: for the condition. The indentation and variable relationships remain the same, but the keywords and punctuation now follow Python’s rules.