DEI Service in CS department at Colgate University
Sustained Dialogue Initiative - a consistent process where people explore hot topics while at the same time building connections in which a safe space and continuous personal growth can happen by focusing on transformational and in-depth conversation practice using Stage 3 and 4 of Intergroup Dialogue (IGD) educational model.
Colgate Women in Computer Science (WiCS) - through mentoring, open hours, career advice, and discussions of women in the CS field, WiCS hopes to create a support system for each other and bring together all class years in the CS department.
At Rutgers University, my colleagues and I studied a dataset spanning over 3.5 academic years, looking at the gender gap, trends related to the gender gap, and how they impact retention in introductory courses.
Monica Babes-Vroman, Isabel Juniewicz, Bruno Lucarelli, Nicole Fox, Thu Nguyen, Andrew Tjang, Georgiana Haldeman, Ashni Mehta, and Risham Chokshi. 2017. Exploring Gender Diversity in CS at a Large Public R1 Research University. In Proceedings of the 2017 ACM SIGCSE Technical Symposium on Computer Science Education (SIGCSE ‘17). Association for Computing Machinery, New York, NY, USA, 51–56.
publications
Completed Work in Large Distributed Scientific Workflows
Journal Papers
Georgiana Haldeman, Ivan Rodero, Manish Parashar, Sabela Ramos, Eddy Z. Zhang, and Ulrich Kremer. 2015. “Exploring energy-performance-quality tradeoffs for scientific workflows with in-situ data analyses”, Computer Science - Research and Development, May 2015, pp. 207-218.
Mehmet Fatih Aktas, Georgiana Haldeman, and Manish Parashar. 2014. “Flexible scheduling and control of bandwidth and in-transit services for end-to-end application workflows”. In Proceedings of the Fourth International Workshop on Network-Aware Data Management (NDM ‘14). IEEE Press, 28–31.
Georgiana Haldeman, Andrew Tjang, Monica Babeş-Vroman, Stephen Bartos *, Jay Shah *, Danielle Yucht *, and Thu D. Nguyen. 2018. Providing Meaningful Feedback for Autograding of Programming Assignments. In Proceedings of the 49th ACM Technical Symposium on Computer Science Education (SIGCSE ‘18). Association for Computing Machinery, New York, NY, USA, 278–283.
Monica Babes-Vroman, Isabel Juniewicz *, Bruno Lucarelli *, Nicole Fox *, Thu Nguyen, Andrew Tjang, Georgiana Haldeman, Ashni Mehta *, and Risham Chokshi *. 2017. Exploring Gender Diversity in CS at a Large Public R1 Research University. In Proceedings of the 2017 ACM SIGCSE Technical Symposium on Computer Science Education (SIGCSE ‘17). Association for Computing Machinery, New York, NY, USA, 51–56.
Joseph A. Boyle *, Georgiana Haldeman, Andrew Tjang, Monica Babes-Vroman, Ana Paula Centeno, and Thu D. Nguyen. 2019. Dynamic Recitation: A Student-Focused, Goal-Oriented Recitation Management Platform. In Proceedings of the 50th ACM Technical Symposium on Computer Science Education (SIGCSE ‘19). Association for Computing Machinery, New York, NY, USA, 1269.
Georgiana Haldeman. 2024. A synthesized teaching model that promotes both procedural and conceptual knowledge dimensions in CS1. In Proceedings of the 8th Conference on Computing Education Practice (CEP ‘24). Association for Computing Machinery, New York, NY, USA, 9–12 (4 pages).
Georgiana Haldeman, Emma Pizer *, Mathelide Hou *, Matthew Rojas *, Kevin Han *, and Ahmed Kamran *. RAVIC: Runtime Analysis Visualizer for Introductory Courses. In Proceedings of the 54th ACM Technical Symposium on Computer Science Education V. 2 (SIGCSE 2023). Association for Computing Machinery, New York, NY, USA, 1391.
Common job descriptions for someone who writes code are programmer, software developer, or software engineer. They are all describing the same job. The variety in names hints at the similarities and differences between software engineering and other engineering. Engineering aims to build something that serves a function, a product, or an artifact.
Most engineering activities involve a design that is separately developed from the product. There are three inter-related main ways in which software engineering differs:
It is malleable (the product can be changed without necessarily changing the design)
It is human-intensive (in regular engineering, the product development involves manufacturing, most often mechanized with little to no involvement from humans)
The quality of the product is more often than not intermixed with that of the design. The right process helps to produce the right product and vice versa.
In fact, it’s more common to hear about the software process vs. the software product in software engineering than design vs. product.
Software Quality
Software Analysis and Testing are very closely related to Software Quality. There are many desirable software qualities in software engineering: correctness, reliability (relative correctness), robustness (reasonable behavior even in circumstances that were not anticipated in the requirements), performance, usability (ease of use), verifiability (we will cover testing in this course), security, maintainability, reusability, portability, understandability (although some applications are less clear than others the design and implementation should not further affect it), interoperability, productivity, timeliness, visibility (transparency of the process, amenable for external examination).
When addressing software quality it’s also useful to consider the different parties: users, developers, and managers because some of the software qualities are visible to the user (we call these external qualities) and some are only visible to the developers of the system (we call these internal qualities). A user wants a product to be reliable, efficient, and easy to use. The developer wants it to be verifiable, maintainable, portable, and extensible, the manager wants the process to be productive, predictable, and easy to control.
Furthermore, while the process is for the most part not visible to the user, the product has user-visible components such as executable code and user manual, and intermediate components (work products or artifacts) such as requirements, design documents, test data, etc. These intermediate components can also be visible to other types of users (other than the end-users).
We conclude that there is a high overlap between external and internal qualities, product and process qualities which makes it hard to standardize it. There are several software quality models that share the same essential aspects but differ in emphasis and organization.
Software Quality Principles
Some general principles to achieve the software qualities discussed prior are:
Rigor and Formality. Rigor means precision and exactness. Formality is a stronger requirement than rigor which requires the use of mathematical laws to evaluate and proceed. It’s impossible to achieve formality in software engineering because of its creative nature so the best we can strive for in many instances is rigor. We will briefly study some formal methods in this course. However, one crucial expectation of the course is that you will practice rigor in your software engineering activities. For example, many students coming into this course are very intuitive about their testing practices. In a few weeks, we will discuss the differences between intuitive and systematic testing. We will then learn different systematic and effective methods to engage in testing. Note that although in the past you were assessed based on the end product (the tests you provided), in this course you will be primarily assessed on the rigor of the process and the reporting.
Separations of concerns
Modularity
Abstraction
Incrementality
Anticipation of change and generality
Code Style
Code style is connected to many desirable software qualities of code, including correctness. Let’s look at this code:
class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown())
cruiseControl.logUnauthorizedAccessAttempt();
if (user.isAstronaut())
cruiseControl.grantAccess(user);
if (user.isCommander())
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
Note that the last line of code is not part of the body of the last if statement and in fact, gets executed every time the method authorize is invoked. For this particular application, this goes beyond correctness and presents a high security risk. The fix is to add curly braces around the body like so:
class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()){
cruiseControl.logUnauthorizedAccessAttempt();
}
if (user.isAstronaut()){
cruiseControl.grantAccess(user);
}
if (user.isCommander()){
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
One may also conclude that the three cases are mutually exclusive and combine them in an if-else-if structure like so:
class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()){
cruiseControl.logUnauthorizedAccessAttempt();
}
else if (user.isAstronaut()){
cruiseControl.grantAccess(user);
}
else if (user.isCommander()){
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
However, note that there is an asymmetry in the code from the fact that we are mixing authorizing code with non-authorizing code. We can increase the understandability of our code by separating them out like so:
class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()) {
cruiseControl.logUnauthorizedAccessAttempt();
return;
}
if (user.isAstronaut()) {
cruiseControl.grantAccess(user);
} else if (user.isCommander()) {
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
One of the examples from last class used an enum structure:
private Result analyzeOrganic(Sample sample) {
if (microscope.isHumanoid(sample) == false) {
return Result.ALIEN;
} else {
return Result.HUMANOID;
}
}
An enumeration is a pre-defined set of values, for example:
public enum Result {
HUMANOID, INORGANIC, ALIEN
}
The advantage to using an enumeration is that when you declare a variable, a parameter or return type of an enumeration type then all the values (stored in the variable, passed to the parameter or returned) must be in the predefined set of the enumeration. For example, analyzeOrganic can only return three values.
Additionally, you can add attributes and constructor to enumerations just like with classes likes so:
public enum Result {
HUMANOID(75),
INORGANIC(40),
ALIEN(100);
final int coolnessIndex;
Result(int newCI){
this.coolnessIndex = newCI;
}
}
Warm-up Problem: How can we improve the quality of this code snippet:
Numbers without an apparent meaning that steer the program are called magic numbers. We can dispel their magic and thus improve the quality of the code by making them constants:
class CruiseControl {
static final int STOP_PRESET = 0;
static final int PLANETARY_SPEED_PRESET = 1;
static final int CRUISE_SPEED_PRESET = 2;
static final double CRUISE_SPEED_KMH = 16944;
static final double PLANETARY_SPEED_KMH = 7667;
static final double STOP_SPEED_KMH = 0;
private double targetSpeedKmh;
void setPreset(int speedPreset) {
if (speedPreset == CRUISE_SPEED_PRESET) {
setTargetSpeedKmh(CRUISE_SPEED_KMH);
} else if (speedPreset == PLANETARY_SPEED_PRESET) {
setTargetSpeedKmh(PLANETARY_SPEED_KMH);
} else if (speedPreset == STOP_PRESET) {
setTargetSpeedKmh(STOP_SPEED_KMH);
}
}
void setTargetSpeedKmh(double speed) {
targetSpeedKmh = speed;
}
}
Can we do better?
Note that nothing restricts the user to call setPreset with values other than the three values it checks for and the function terminates without error. We can make the semantics of this function more transperant for outside usage by using an enum construct like so:
Conventions are rules that are widely agreed upon by professionals. Used approapriately and consistently, they can help with the readability of the code.
Rule 1: Remove excessive comments for example:
class Inventory {
// Fields (we only have one)
List<Supply> supplies = new ArrayList<>(); // The list of supplies.
// Methods
int countContaminatedSupplies() {
// TODO: check if field is already initialized (not null)
int contaminatedCounter = 0; // the counter
// No supplies => no contamination
for (Supply supply : supplies) { // begin FOR
if (supply.isContaminated()) {
contaminatedCounter++; // increment counter!
} // End IF supply is contaminated
}// End FOR
// Returns the number of contaminated supplies.
return contaminatedCounter; // Handle with care!
}
} // End of Inventory class
Becomes:
class Inventory {
List<Supply> supplies = new ArrayList<>();
int countContaminatedSupplies() {
if (supplies == null || supplies.isEmpty()) {
// No supplies => no contamination
return 0;
}
int contaminatedCounter = 0;
for (Supply supply : supplies) {
if (supply.isContaminated()) {
contaminatedCounter++;
}
}
return contaminatedCounter;
}
}
Rule 2: Remove code in comments for example:
class LaunchChecklist {
List<String> checks = Arrays.asList(
"Cabin Leak",
// "Communication", // Do we actually want to talk to Houston?
"Engine",
"Hull",
// "Rover", // We won't need it, I think...
"OxygenTank"
//"Supplies"
);
Status prepareLaunch(Commander commander) {
for (String check : checks) {
boolean shouldAbortTakeoff = commander.isFailing(check);
if (shouldAbortTakeoff) {
//System.out.println("REASON FOR ABORT: " + item);
return Status.ABORT_TAKE_OFF;
}
}
return Status.READY_FOR_TAKE_OFF;
}
}
Becomes:
class LaunchChecklist {
List<String> checks = Arrays.asList(
"Cabin Leak",
"Engine",
"Hull",
"OxygenTank"
);
Status prepareLaunch(Commander commander) {
for (String check : checks) {
boolean shouldAbortTakeoff = commander.isFailing(check);
if (shouldAbortTakeoff) {
return Status.ABORT_TAKE_OFF;
}
}
return Status.READY_FOR_TAKE_OFF;
}
}
Rule 3: Replace comments with constants for example:
enum SmallDistanceUnit {
CENTIMETER,
INCH;
double getConversionRate(SmallDistanceUnit unit) {
if (this == unit) {
return 1; // identity conversion rate
}
if (this == CENTIMETER && unit == INCH) {
return 0.393701; // one centimeter in inch
} else {
return 2.54; // one inch in centimeters
}
}
}
Becomes:
enum SmallDistanceUnit {
CENTIMETER,
INCH;
static final double INCH_IN_CENTIMETERS = 2.54;
static final double CENTIMETER_IN_INCHES = 1 / INCH_IN_CENTIMETERS;
static final int IDENTITY = 1;
double getConversionRate(SmallDistanceUnit unit) {
if (this == unit) {
return IDENTITY;
}
if (this == CENTIMETER && unit == INCH) {
return CENTIMETER_IN_INCHES;
} else {
return INCH_IN_CENTIMETERS;
}
}
}
Rule 4: Replace comments with utility methods for example:
class FuelSystem {
List<Double> tanks = new ArrayList<>();
int getAverageTankFillingPercent() {
double sum = 0;
for (double tankFilling : tanks) {
sum += tankFilling;
}
double averageFuel = sum / tanks.size();
// round to integer percent
return Math.toIntExact(Math.round(averageFuel * 100));
}
}
Becomes:
class FuelSystem {
List<Double> tanks = new ArrayList<>();
int getAverageTankFillingPercent() {
double sum = 0;
for (double tankFilling : tanks) {
sum += tankFilling;
}
double averageFuel = sum / tanks.size();
return roundToIntegerPercent(averageFuel);
}
static int roundToIntegerPercent(double value) {
return Math.toIntExact(Math.round(value * 100));
}
}
Rule 5: Do document implementation decisions using a pattern like this:
/*
* In the context of [USE CASE],
* facing [CONCERN]
* we decided for [OPTION]
* to achieve [QUALITY],
* accepting [DOWNSIDE].
*/
For example:
class Inventory {
private List<Supply> list = new ArrayList<>();
void add(Supply supply) {
list.add(supply);
Collections.sort(list);
}
boolean isInStock(String name) {
// fast implementation
return Collections.binarySearch(list, new Supply(name)) != -1;
}
}
Becomes:
boolean isInStock(String name) {
/*
* In the context of checking availability of supplies by name,
* facing severe performance issues with >1000 supplies
* we decided to use the binary search algorithm
* to achieve item retrieval within 1 second,
* accepting that we must keep the supplies sorted.
*/
return Collections.binarySearch(list, new Supply(name)) != -1;
}
Rule 6: Use examples in your documentation for instance:
class Supply {
/**
* The code universally identifies a supply.
*
* It follows a strict format, beginning with an S (for supply), followed
* by a five digit inventory number. Next comes a backslash that
* separates the country code from the preceding inventory number. This
* country code must be exactly two capital letters standing for one of
* the participating nations (US, EU, RU, CN). After that follows a dot
* and the actual name of the supply in lowercase letters.
*/
static final Pattern CODE =
Pattern.compile("^S\\d{5}\\\\(US|EU|RU|CN)\\.[a-z]+$");
}
Becomes:
class Supply {
/**
* The expression universally identifies a supply code.
*
* Format: "S<inventory-number>\<COUNTRY-CODE>.<name>"
*
* Valid examples: "S12345\US.pasta", "S08342\CN.wrench",
* "S88888\EU.laptop", "S12233\RU.brush"
*
* Invalid examples:
* "R12345\RU.fuel" (Resource, not supply)
* "S1234\US.light" (Need five digits)
* "S01234\AI.coconut" (Wrong country code. Use US, EU, RU, or CN)
* " S88888\EU.laptop " (Trailing whitespaces)
*/
static final Pattern SUPPLY_CODE =
Pattern.compile("^S\\d{5}\\\\(US|EU|RU|CN)\\.[a-z]+$");
}
This is a description of your tutorial, note the different field in type. This is a markdown files that can be all markdown-ified like any other post. Yay markdown!