This repository contains the notes I collected while studying the Udemy course.
The notes summarize the main rules and examples from the course. The examples are written in Java for practice.
Course: Clean Code in Arabic - Udemy
Instructor: Mohamed Hassan
Language: Arabic
Duration: 1 hour, 19 lectures
The file is organized to reflect the course structure with full content in each part:
All rules with bad vs good examples.
Includes large multi-line bad examples, corrected code, DRY, dependency magnets, and exception usage.
All content preserved including journal logs, commented-out code, and best practices.
Full details on exceptions, including checked vs unchecked, fail fast, rollback, cleanup, re-throw, and what to avoid.
Full certificate visual included.
Each section contains the full theory, all bad vs good examples, and practical key takeaways.
Bad:
int d;Good:
int elapsedTimeInDays;The good name explains exactly what the value represents.
Bad:
private Date modymdhms;Good:
private Date modificationFullDate;A pronounceable name is easier to read, discuss, and remember.
Bad:
PhoneNumber phoneString;Good:
PhoneNumber phone;Bad:
private String m_dsc;Good:
private String description;Avoid adding unnecessary prefixes, type hints, or unclear abbreviations to names.
Good examples:
Customer
Account
UserBad examples:
Manage
Calculating
DrawClasses represent things, so their names should usually be nouns.
Good examples:
deletePage()
getName()
save()Bad examples:
Customer
Account
UserMethods represent actions, so their names should usually be verbs.
Names should be descriptive, readable, and follow Java conventions. Classes represent things, while methods represent actions.
Keep the number of lines and parameters low to improve readability.
Bad example:
public String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) {
boolean isTestPage = pageData.hasAttribute("Test");
if (isTestPage) {
WikiPage testPage = pageData.getWikiPage();
StringBuilder newPageContent = new StringBuilder();
includeSetupPages(testPage, newPageContent, isSuite);
newPageContent.append(pageData.getContent());
includeTeardownPages(testPage, newPageContent, isSuite);
pageData.setContent(newPageContent.toString());
}
return pageData.getHtml();
}Good example:
public String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) {
if (isTestPage(pageData)) {
includeSetupAndTeardownPages(pageData, isSuite);
}
return pageData.getHtml();
}The good version hides the details behind meaningful helper methods.
Bad example:
if (id != null) {
printStudent();
logStudentAccess();
updateLastSeenDate();
} else {
printError();
logError();
}Good example:
if (id != null) {
handleExistingStudent();
} else {
handleMissingStudent();
}The main point is that each block should stay short and easy to understand.
Bad example:
public void addEmployee() {
Employee employee = new Employee("Mohamed", "Hassan");
saveUserToDatabase(employee);
printSuccessMessage();
}Good example:
public boolean addEmployee() {
Employee employee = new Employee("Mohamed", "Hassan");
return saveUserToDatabase(employee);
}The good version focuses on one clear responsibility.
Bad example:
if (deletePage(page) == ErrorCode.ERROR) {
// handle error
}Good example:
try {
deletePage(page);
registry.deleteReference(page.getName());
configKeys.deleteKey(page.getName().makeKey());
} catch (Exception e) {
logger.log(e.getMessage());
}Exceptions make the main flow cleaner and prevent callers from forgetting to check error codes.
Bad example:
private static void doSomething() {
// repeated logic
}
private static void doSomethingAgain() {
// same repeated logic
}Good example:
private static void printEmployee(Employee employee) {
String format = "%s is %s, lives in %s, age %d";
System.out.println(String.format(
format,
employee.getFirstName(),
employee.getRelation(),
employee.getAddress(),
employee.getAge()
));
}Repeated logic should be centralized in one place.
Bad idea: returning error codes.
enum ErrorCode {
OK,
INVALID,
LOCKED,
WAITING
}Problems:
- Any function using this enum becomes tightly coupled to it.
- Adding new codes may require recompiling dependent code.
Better idea: use exceptions instead.
Benefits:
- New exception types can inherit from a base exception class.
- New exceptions can be added without forcing all dependent code to change.
Functions should be small, focused, readable, and do only one thing. Keep blocks short, use exceptions for errors, and centralize repeated logic to follow DRY.
Comments should not be used to explain unclear code that could be improved by better naming, smaller functions, or clearer structure.
Use meaningful names, small functions, and clear logic so the code explains itself as much as possible.
Good comments include:
- Legal comments: copyright or license notices.
- Clarification comments: explain tricky logic.
- Documentation comments: public API descriptions or JavaDoc.
- Informative comments: extra context about usage.
- TODO comments: temporary notes that should be reviewed later.
Avoid keeping long historical logs inside the code.
/* Changes (from 11-Oct-2001)
* 05-Nov-2018 : Added a getDescription() method, and eliminated NotableDate class (DG);
* 12-Nov-2018 : IBD requires setDescription() method, now that NotableDate class is gone (DG); Changed getPreviousDayOfWeek(), getFollowingDayOfWeek() and getNearestDayOfWeek() to correct bugs (DG);
* 05-Dec-2019 : Fixed bug in SpreadsheetDate class (DG);
* 29-May-2020 : Moved the month constants into a separate interface (MonthConstants) (DG);
* 29-May-2020 : Fixed bug in addMonths method (DG);
* 04-Sep-2020 : Implemented Comparable. Updated the isInRange javadocs (DG);
* 05-Jan-2020 : Fixed bug in addYears() method (150025) (DG);
*/Avoid leaving old commented-out code in the file.
//private static void printEmployee(Employee employee)
//{
// String format = "%s is %s, lives in %s, age %d";
// System.out.println(String.format(
// format,
// employee.getFirstName(),
// employee.getRelation(),
// employee.getAddress(),
// employee.getAge()
// ));
//}Avoid cluttering your code with historical logs or commented-out code. Comments should clarify, not replace refactoring or meaningful code.
This section summarizes Java exceptions, when to throw them, when to catch them, and what to avoid while handling them.
In Java, when an error occurs inside a method, the method creates an exception object and throws it to the runtime system.
There are two main categories:
- Checked exceptions: must be declared or handled.
Examples:
IOException
SQLException- Unchecked exceptions: runtime exceptions that do not require explicit handling.
Examples:
NullPointerException
ArithmeticExceptionImportant base classes:
java.lang.Exception
java.lang.RuntimeExceptionjava.lang.Exception is the base class for exceptions, and java.lang.RuntimeException is the base class for unchecked exceptions.
static void findWinner(int[] winners) {
if (winners == null) {
throw new IllegalArgumentException("Parameter 'winners' cannot be null");
}
otherMethodThatUsesTheArray(winners);
}Benefits:
- Prevent continuing execution with invalid inputs.
- Save resources.
- Make debugging easier.
void writeLog(File logFile) {
if (!logFile.canWrite()) {
throw new IllegalStateException("Log file cannot be written to because it is read-only");
}
// Write data to the log file
}Always validate object state before operations to prevent invalid results or failures.
static int getValueFromArray(int[] array, int index) {
try {
return array[index];
} catch (ArrayIndexOutOfBoundsException ex) {
throw ex;
}
}Avoid throwing generic exceptions or creating new ones when the framework already provides a suitable exception.
Throw exceptions instead of returning error codes.
Reason:
- Calling code may forget to check the returned error code.
- Exceptions make failure clearer and harder to ignore.
if (conn.getState() != ConnectionState.CLOSED) {
conn.close();
}Avoid using exceptions for normal control flow.
Reason:
- Exception creation is expensive.
- Stack trace collection using
fillInStackTraceis expensive. - Exceptions should be used only for exceptional situations.
try {
File file = new File(filePath);
Scanner scanner = new Scanner(file);
} catch (FileNotFoundException e) {
System.out.println("File not found, please enter another path.");
promptUserForAnotherFilePath();
}Catch the exception only if you can do something useful with it.
Specific exceptions should come before general exceptions.
Bad example:
try {
int value = numbers[index];
} catch (Exception e) {
System.out.println("General error");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid index");
}Good example:
try {
int value = numbers[index];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid index");
} catch (Exception e) {
System.out.println("General error");
}The generic exception should not be placed before the specific exception.
try {
File file = new File(filePath);
Scanner scanner = new Scanner(file);
} catch (FileNotFoundException e) {
logger.log(Level.SEVERE, "File not found", e);
throw e;
}Log and clean up if needed, but re-throw if you cannot fully handle the exception.
try {
account.deposit(amount);
dbConnection.save();
} catch (Exception e) {
dbConnection.rollback();
throw e;
} finally {
closeResources();
}Use rollback and cleanup to keep the system in a consistent state.
try {
mediator.send(new UpdatePostCommand(viewModel));
return view(viewModel);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error updating post", e);
return view(viewModel);
}Only swallow exceptions at the topmost layer, such as UI or API layer. Lower layers should usually re-throw.
Do not use exceptions to control normal program logic.
Good example:
boolean isValid = validateProduct(product);
if (!isValid) {
return view(product);
}
createProduct(product);
return view(product);Exceptions should signal truly unexpected errors.
Avoid passing or returning exceptions as normal parameters or return values.
Use this only in specialized patterns, such as an exception factory.
Exception analyzeHttpError(int errorCode) {
if (errorCode < 400) {
throw new NotAnErrorException();
}
switch (errorCode) {
case 403:
return new ForbiddenException();
case 404:
return new NotFoundException();
case 500:
return new InternalServerErrorException();
default:
return new UnknownHttpErrorCodeException(errorCode);
}
}Never leave empty catch blocks.
Bad example:
try {
processData();
} catch (Exception e) {
// Do nothing - avoid this
}Empty catch blocks hide bugs and make debugging harder.
finally blocks are used to always clean up resources, regardless of exceptions.
FileOutputStream file = null;
try {
file = new FileOutputStream("file.txt");
file.write(0xFF);
} finally {
if (file != null) {
file.close();
}
}Validate inputs, fail fast, throw specific exceptions, catch only when you can handle the error meaningfully, clean up resources, and never use exceptions for normal control flow.