# Validate Email

> Looks like an email. But is it?

Canonical URL: <https://datadriven.io/problems/validate_email>

Domain: Python · Difficulty: medium · Seniority: L3

## Problem

Return True when the input string has: exactly one '@' character with at least one character before it, at least one '.' somewhere after the '@' with at least one character before and after the dot. Otherwise return False.

## Worked solution and explanation

### Why this problem exists in real interviews

Email validation looks like a one-liner regex problem, but interviewers use it to see whether you can translate a fuzzy spec into deterministic boolean logic without leaning on libraries. The hidden goal is watching how you handle edge inputs (empty string, multiple '@', dot before '@', dot at the very end) and whether you state the rules back before coding.

---

### Break down the requirements

#### Step 1: Exactly one '@' with a non empty local part

Use `email.count('@') == 1` then split. If the part before '@' is empty, the email is invalid. Counting first is cheaper than catching a `ValueError` from `split` and keeps the intent explicit.

#### Step 2: At least one '.' strictly after the '@'

After splitting, search the domain side for a '.'. Using `'.' in domain` is correct only after you confirm there is a domain. Reject empty domain immediately to avoid an indexing surprise.

#### Step 3: Non empty labels around the dot in the domain

The dot cannot be the first or last character of the domain. Slice on the first dot (or rsplit on the last) and confirm both sides are non empty. This is what trips most candidates: `'a@.b'` and `'a@b.'` both look superficially fine.

---

### The solution

**Boolean rules without a regex**

```python
def is_valid_email(email: str) -> bool:
    if email.count('@') != 1:
        return False
    local, domain = email.split('@')
    if not local or not domain:
        return False
    if '.' not in domain:
        return False
    head, _, tail = domain.partition('.')
    return bool(head) and bool(tail)
```

> **Cost Analysis**
>
> Time is O(n) where n is the length of the input string because `count`, `split`, and `partition` each scan once. Space is O(n) for the two split pieces. There is no need for backtracking or a compiled regex.

> **Interviewers Watch For**
>
> Restating the spec before coding (especially the 'character before and after the dot' clause), naming the four invalid shapes you intend to reject, and avoiding `try/except` as control flow when a simple count check is clearer.

> **Common Pitfall**
>
> Using `'.' in email` instead of `'.' in domain` accepts strings like `'a.b@c'` where the dot is on the wrong side of the '@'. Always validate the dot relative to the domain, not the whole string.

---

## Common follow-up questions

- How would you also reject local parts that start or end with a dot? _(Most real validators forbid `.foo@bar.com` and `foo.@bar.com`. Add `local[0] != '.' and local[-1] != '.'` and discuss why the spec did not require it here.)_
- What changes if the domain is allowed to have multiple dots like `a@b.c.d`? _(Switch from `partition` to `rsplit('.', 1)` so the last dot defines the TLD, and confirm every label between dots is non empty.)_
- Would you implement this with `re.fullmatch` in production? _(Compare maintainability of a single regex against the readable rule list. Interviewers want to hear that regex is fine when the spec is stable but rule lists are easier to extend.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/validate_email)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). 100% free data engineering interview prep. Live code execution against Postgres 16, Python 3.11, and Spark sandboxes. No paywall, no premium tier, no signup gate.